외대 기숙사·자취 룸메이트를 구할 때 에브리타임 게시글이 사실상 유일한 방법이지만, 생활 패턴을 제대로 확인하기 어려워 입주 후 갈등이 자주 발생한다는 문제에서 시작했습니다.
수면 시간, 청소 빈도, 흡연 여부 등 12가지 라이프스타일 항목을 3단계 온보딩으로 수집해 취침 시간(3) × 성향(5) = 15가지 유형으로 자동 분류하고, Gemini AI가 우선순위 가중치를 반영해 추천하는 매칭 서비스를 만들었습니다.
| Domain | User, Lifestyle, Like, Match, Notification, Type, UserPriorityTrait, UserSleepHabit, UserSleepSensitivity |
| Enum | 15개 (CleaningFrequency, Gender, MatchStatus, Personality, SleepTime, TraitKey 등) |
| Controller | Auth, Like, Main, Matching, Mypage, Notification, Onboarding, Search, UserProfile |
| DTO | 30개 이상 |
| Config | DataInitializer, GeminiProperties, SecurityConfig |
JWT 기반 이메일 로그인을 구현하며 @hufs.ac.kr 도메인 검증, BCrypt 암호화, 이메일·닉네임 중복 확인 로직을 만들었습니다. Docker + GitHub Actions로 자동 배포 파이프라인을 구축했고, SecurityConfig와 SwaggerConfig로 HTTPS 서버 설정을 관리했습니다.
Step1(선호 층수·성격·온도), Step2(샤워시간·청소빈도·수면시간·수면민감도·수면습관), Step3(방 내 식사·흡연·통화·귀가 빈도)까지 총 12가지 항목을 수집해 Lifestyle 엔티티에 저장하는 API를 만들었습니다.
취침 시간(EARLY / LATE / MIDNIGHT) × 성향(NEAT / SOCIAL / QUIET / FREE / HERMIT) 조합으로 15가지 유형 분류 로직을 직접 설계하고 구현했습니다. DataInitializer에서 서버 기동 시 15개 타입을 자동 초기화하고 S3 URL로 이미지를 매핑하도록 했습니다.
라이프스타일과 우선순위 항목(TraitKey)을 프롬프트로 구성하고, 우선순위 항목에는 3배 가중치를 적용했습니다. Gemini 응답을 매칭 점수 JSON으로 파싱해 RankedCandidate로 캐싱하고 AI 추천 리스트로 응답하도록 했으며, 매칭 요청에 카카오 오픈채팅 URL을 포함하는 기능도 추가했습니다.
좋아요 → 매칭 요청 → 수락 / 거절 / 완료까지 전 단계를 구현했고, 매칭 완료 시 양측 UserStatus 변경과 Notification 자동 생성을 처리했습니다. 성별·라이프스타일 전체 필터 기반 유저 검색을 전면 재설계했고, 마이페이지에서 프로필·라이프스타일 수정 시 유형이 자동 재계산되도록 만들었습니다.
백엔드 개발과 함께 서비스 화면 디자인을 맡아, 온보딩부터 추천·매칭까지 이어지는 사용자 흐름을 화면으로 설계했습니다.
추천 목록에서 프로필 이미지와 유형(type)이 null로 표시됐습니다.
RankedCandidate에 User 엔티티를 통째로 캐시해, 트랜잭션 종료 후 detached 상태에서 user.getType()에 접근하는 게 실패했습니다.
트랜잭션 안에서 imageUrl과 typeCode를 원시값으로 미리 계산해 캐시에 저장해, detached 상태에서도 정상 반환되도록 했습니다.
좋아요 API 호출 시 400 에러가 발생하고 receiverId가 null로 들어왔습니다.
응답 DTO는 snake_case(user_id)로 내려주는데 요청 DTO는 camelCase(receiverId)를 기대해, 프론트에서 user_id로 보내면 바인딩에 실패했습니다.
@JsonAlias({"user_id", "userId", "targetUserId"})로 다중 필드명을 매핑했습니다.
검색, 좋아요, AI 추천 화면에서 이미지가 표시되지 않았습니다.
open-in-view=false 환경에서 LAZY 연관관계(Like → likedUser → type) 체인이 null로 끊겼습니다.
LikeRepository에 LEFT JOIN FETCH u.type 쿼리를 추가해 로딩 체인 전체를 즉시 로딩으로 전환했습니다.
PUT /mypage/profile/modify 호출 후 GET /users/{id}에서 수정값이 반영되지 않았습니다.
DTO에 @Setter가 누락돼 Jackson 역직렬화가 실패해 모든 필드가 null 처리됐고, JPA dirty checking에 의존해 repository.save() 호출도 빠져 있었습니다.
@Setter를 추가하고 repository.save()를 명시적으로 호출하도록 수정했습니다.
좋아요 목록 API가 likes 테이블이 아닌 matches 테이블을 조회하고 있었습니다.
Like 테이블을 분리하고 LikeRepository에 findAllByUserId를 추가해, MainController에서 getLikeList를 호출하도록 교체했습니다.
2026-08-15 기준, EC2 서버에서 실측한 값입니다.