카테고리 없음

신입 개발자의 첫 실무 도전기: 얼굴인식 + 예약관리 서비스 구축

taehyuck 2025. 11. 30. 22:58
728x90

코쿤부스 예약 관리 프로젝트 개발기

입사 일주일 만에 맡은 첫 실무 프로젝트

프로젝트 시작한 지 이제 2주 정도. 입사하자마자 “한 번 맡아보라”는 말을 들었을 때, 기대와 걱정이 동시에 밀려왔다.
특히 나는 백엔드 중심 개발자라 프론트엔드 디자인 감각이 좋지 않다는 피드백을 자주 들어서, 실제 화면을 만들 생각에 가장 긴장됐다.


1. 프로젝트 개요

  • 프론트엔드: React
  • Web Backend API: Spring Boot
  • AI·얼굴인식 API: FastAPI
  • DB & 배포: 추후 클라우드 환경 포함 예정
  • 기능: 회원가입 + 얼굴 인증, 예약·취소, 관리자 승인, 로그 저장 등

FastAPI 하나로 웹 API까지 모두 처리해도 됐지만,
장기적인 고도화(확장성)를 고려해서 웹 API는 Spring Boot로 분리했다.


2.  프로젝트 시작: 가장 먼저 API부터 만들었다

백엔드는 익숙하기 때문에 Spring Boot 기반 API는 몇 시간 만에 프로토타입 완성했다.
로그인, 회원가입, 예약 조회, 예약 생성 정도는 금방 뽑았다.

문제는 프론트였다.


3.  프론트엔드 디자인, 하루가 사라지는 경험…

React로 구현하는 것 자체는 어렵지 않았는데,
"디자인을 어떻게 할 것인가"가 진짜 문제였다.

원하는 UI/UX를 만들기 위해 계속 수정하고,
CSS를 고치고, margin 하나에 수십 번 새로고침하면서 하루가 다 지나갔다.

이때 깨달음
프론트는 단순히 코드가 아니라 '감각'과 '구현력'이 동시에 필요하다.

 

 

 

하지만 확실히 하루 동안 엄청 배웠다.
오히려 지금은 더 빠르게 화면을 뽑아낼 자신도 생겼다.

 

 


4. 회원가입: 얼굴을 이용한 중복 가입 방지

가장 특징적인 기능은 OpenCV로 얼굴을 촬영해 회원가입 시 얼굴 정보를 저장한 것.

AI 센터에 둘거라서 오는 사람들로 하여금 미래 지향적인 느낌을 주고 싶었다.

FastAPI에서 OpenCV를 이용해 사용자의 얼굴을 분석하고,
이미 등록된 얼굴과 유사도가 높으면 가입을 막도록 구현했다.

 FastAPI 얼굴 분석 예시 코드

 
 
# /app/face_check.py
import cv2
import numpy as np

def is_duplicate_face(new_face_img, saved_face_img):
    new = cv2.imread(new_face_img, cv2.IMREAD_GRAYSCALE)
    saved = cv2.imread(saved_face_img, cv2.IMREAD_GRAYSCALE)

    orb = cv2.ORB_create()
    kp1, des1 = orb.detectAndCompute(new, None)
    kp2, des2 = orb.detectAndCompute(saved, None)

    bf = cv2.BFMatcher(cv2.NORM_HAMMING, crossCheck=True)
    matches = bf.match(des1, des2)

    score = sum([m.distance for m in matches]) / len(matches)
    return score < 50  # 임계값

 

이 기능은 실무에서 꽤 신선해했다.
다만 고민되는 부분이 있었다.


5. 고민 : 쌍둥이처럼 닮은 사람은 어떻게 해야 할까?

얼굴 인식 기반 중복 가입 방지가 좋긴 하지만,
쌍둥이나 아주 비슷하게 생긴 사람은 가입이 막힐 수 있다는 문제가 있다.

민원 가능성도 있고, 너무 강한 인증은 "가벼운 프로젝트"에 맞지 않을 수 있다.

PASS 같은 본인인증 연동도 생각했지만
- 신입에, 작은 프로젝트에, 회사에서도 그렇게까지는 원하지 않는 상황.

그래서 이 문제는
"고도화 2차 단계에서 해결할 과제로 보류"하기로 했다.


6. 보안 강화 : 세션 로그인이었지만 JWT를 추가했다

원래는 쿠키 + 세션 기반 로그인만 구현했었다.
하지만 예약 시스템은 다른 사용자 예약을 취소하면 큰 사고가 될 수 있다.

그래서 다음과 같이 설계를 변경했다.

  • 쿠키 + 세션 → 기본 로그인 유지
  • JWT → 예약·취소·승인 API 접근 시 필요하도록 추가

즉, 이중 보안 구조를 사용했다.

Spring Boot JWT 필터 예시

 
// src/main/java/com/project/security/JwtFilter.java
public class JwtFilter extends OncePerRequestFilter {

    @Override
    protected void doFilterInternal(HttpServletRequest request,
                                    HttpServletResponse response,
                                    FilterChain filterChain) throws ServletException, IOException {

        String token = request.getHeader("Authorization");

        if (token != null && JwtUtils.validateToken(token)) {
            Authentication auth = JwtUtils.getAuthentication(token);
            SecurityContextHolder.getContext().setAuthentication(auth);
        }

        filterChain.doFilter(request, response);
    }
}

7. 운영을 고려한 개발: N+1 해결, 로그 저장, 성능 고민

팀에서 "실제로 상용화될 가능성이 있다"고 하셔서
운영단계를 염두에 두고 개발했다.

  • Spring Data JPA의 N+1 문제 해결 (fetch join, EntityGraph 적용)
  • 요청 로그·AI 로그 저장하도록 구성
  • fastapi와 spring 각각의 성능 모니터링 구조 구성
  • 예약 API 성능 튜닝

아직 실제 대규모 트래픽은 받아보지 않았지만
최소한 구조적으로는 준비해 둔 셈이다.

 

 


8. 배포 고민: 클라우드에서 어떻게 구성할까?

지금 가장 고민되는 점은 배포 구조다.

가능한 옵션은 다음 3가지:

  1. 서버 한 대에 React + Spring + FastAPI 모두 넣기 (가격과 가장 편리함, 하지만 장애에 취약)
  2. Spring / FastAPI 분리, React는 S3 또는 Storage에 정적 호스팅
  3. 서버 2대 구성 후, API Load Balancer까지 확장 고려

회사에서 어떤 선택을 할지는 아직 모르지만,
나는 2번 또는 3번을 추천드릴 생각이다.

로드밸런서가 엄청나게 큰 규모의 프로젝트에서도 달에 10만원 정도만 나온다고 하고

서버는 내부망에서만 접근할 수 있어 보안 수준이 향상된다.


9. 회고 : 한 주 동안의 성장

이 프로젝트는 단순한 CRUD 시스템이 아니다.
AI + 인증 + 예약 로직 + 프론트 + 배포까지 혼자 맡은 첫 실무 프로젝트였다.

가장 큰 배움은 다음과 같다.

  • 백엔드만 하던 내가 프론트·디자인의 난이도를 직접 체감했다.
  • 빠르게 기능을 개발하는 것보다,
    사용자가 편하게 느끼는 화면을 만드는 것이 훨씬 어렵다는 것을 알았다.
  • 실제 운영을 고려한 성능·예외 처리·보안 구조를 경험했다.
  • 기술 선택은 결국 “현재와 미래” 둘 다를 봐야 한다는 걸 배웠다.

앞으로는 얼굴인식 정교화, PASS 본인인증 연동,
운영 환경에서의 부하테스트 등 해야 할 것이 많다.

하지만 이 프로젝트를 통해 확실히 한 단계 성장한 느낌이다.
첫 실무 프로젝트 치고는 꽤 좋은 경험이라 생각한다.


마무리

앞으로도 기능 추가와 고도화를 계속하면서
내가 만든 시스템이 실제 운영되는 경험을 반드시 해보고 싶다.

혹시 구현 과정이나 소스 구조에 대해 궁금한 점이 있다면
댓글로 남겨주시면 최대한 상세하게 기록해볼 생각이다.

728x90