Skip to content

release-260311.01#285

Merged
seulnan merged 4 commits into
mainfrom
develop
Mar 10, 2026
Merged

release-260311.01#285
seulnan merged 4 commits into
mainfrom
develop

Conversation

@seulnan

@seulnan seulnan commented Mar 10, 2026

Copy link
Copy Markdown
Collaborator

📄 작업 내용 요약

📎 Issue 번호

seulnan added 4 commits March 11, 2026 02:40
getPendingReservationStatuses에서 조회자(userId)가 아닌 메일 발신자 계정 기준으로 OAuth 토큰을 갱신하도록 수정합니다.
특권 유저/HR이 팀원 예약을 조회할 때 각각의 발신자 계정 상태가 정확히 반영되도록 합니다.

Made-with: Cursor
getUserMailReservation에서 isPrivilegedUser 결과를 캐싱해 특권 유저의 불필요한 팀 이메일 계산을 방지합니다.
특권 유저는 항상 접근 허용, 일반 유저는 같은 팀 발신자 이메일인지 여부로만 접근을 결정하도록 분기 구조를 단순화합니다.

Made-with: Cursor
@seulnan seulnan self-assigned this Mar 10, 2026
@seulnan

seulnan commented Mar 10, 2026

Copy link
Copy Markdown
Collaborator Author

@claude 이 릴리즈에 대해 정리하고 리뷰해줘.

@github-actions

github-actions Bot commented Mar 10, 2026

Copy link
Copy Markdown

Claude finished @seulnan's task in 2m 42s —— View job


릴리즈 PR 코드 리뷰 (release-260311.01)

작업 목록

  • 컨텍스트 파악
  • 변경 파일 분석
  • 코드 리뷰 작성

📋 릴리즈 변경 내용 요약

이번 릴리즈는 크게 두 가지 기능 영역의 변경을 포함합니다.

영역 주요 변경
메일 예약 권한 체계 특권 유저(HR 파트 / privilegedEmails) 및 팀 단위 메일 조회 기능 도입
ATS 지원자 처리 ApplicantSyncMapping 기반의 새로운 오버로드 메서드 추가

변경된 파일 (16개):

  • MailService.kt (+77/-25): 핵심 서비스 로직 — 권한 분기, retryReservation 추가
  • MemberPrivacyService.kt (+24): 신규 서비스 — 특권 유저 판별, 팀 이메일 목록 조회
  • MailReservationReader.kt (+20): 다수 이메일 기반 조회 메서드 추가
  • MailReservationRepository.kt / JpaMailReservationRepository.kt / MailReservationRepositoryImpl.kt: 쿼리 확장
  • ActiveMemberRepository.kt / ActiveMemberRepositoryImpl.kt / JpaActiveMemberRepository.kt / JpaMemberPartRepository.kt: 멤버 ID 목록 조회 추가
  • FormResponseToApplicantProcessor.kt: ApplicantSyncMapping 기반 오버로드 추가
  • MailReservationDetailResponse.kt / MailReservationListResponse.kt: 발신자 이메일 / 대표 수신자 필드 추가
  • MailServiceTest.kt (+86): 새 테스트 케이스 추가
  • mail-reservation-queries.sql (+56/-35): 디버깅용 SQL 쿼리 수정

🔴 중요 이슈

1. 빈 이메일 목록에서 IN 쿼리 에러 발생 가능성

MailService.kt L70–74, L245–246:

val senderEmails = memberPrivacyService.getActiveTeamMemberEmails(userId).toList()
mailReservationReader.readAllBeforeBySenderEmails(now, senderEmails)

getActiveTeamMemberEmails가 최소 본인 이메일을 반환하므로 비어있지 않지만, JpaMailReservationRepositoryIN :senderEmails 쿼리에 빈 컬렉션이 전달될 경우 일부 DB/JPA 구현에서 SQL 에러가 발생할 수 있습니다. findAllBySenderEmails, findAllByReservationTimeLessThanEqualAndSenderEmails 모두 빈 목록 방어 로직이 없습니다.

Fix this →

2. 프로덕션 코드에 개인 이메일 하드코딩 (보안)

MemberPrivacyService.kt L16–22:

private val privilegedEmails: Set<String> = setOf(
    "umi.urssu@gmail.com",
    "feca.urssu@gmail.com",
    ...
)

실제 팀원 이메일이 소스코드에 하드코딩되어 있습니다. 이는 다음 문제를 야기합니다:

  • 팀원 변경 시 코드 수정 + 재배포 필요
  • 개인정보 노출 (Git 이력 포함)
  • application.yml 혹은 DB 설정으로 분리하는 것을 권장합니다.

Fix this →


🟡 주의 이슈

3. N+1 쿼리 문제 — getActiveTeamMemberEmails

MemberPrivacyService.kt L50, ActiveMemberRepositoryImpl.kt L36–39:

val activeMembers = memberReader.readAllActive()
// 내부적으로 멤버 수만큼 findAllPartsByMemberId() 쿼리 실행

findAll()map { fetchWithParts(it) } 구조로 인해 Active 멤버 수만큼 파트 조회 쿼리가 추가 발생합니다. 멤버가 많아지면 성능 저하 우려가 있습니다. JOIN FETCH를 활용한 단일 쿼리로 개선을 고려해 주세요.

4. JPQL 암시적 조인 사용

JpaMailReservationRepository.kt L18–50:

"SELECT r FROM MailReservationEntity r, MailEntity m WHERE r.mailId = m.id ..."

Cartesian product 후 WHERE로 필터하는 암시적 조인 방식입니다. JPQL 표준의 명시적 JOIN / JOIN FETCH 사용이 권장됩니다. 다만 현재 MailReservationEntityMailEntity에 대한 연관 관계가 없어 (mailId만 저장) 이 방식을 사용한 것으로 보입니다 — 연관 관계 매핑 또는 native query 사용을 고려해 볼 수 있습니다.

5. retryReservation 권한 체크 범위 불일치

MailService.kt L171–173:

val senderEmail = user.getEmailAddress()
if (mail.senderEmailAddress != senderEmail) {
    throw MailReservationAccessDeniedException(...)
}

getUserMailReservation, getUserMailReservations는 특권 유저가 전체 조회 가능하도록 설계됐지만, retryReservation은 소유자 본인만 재시도 가능합니다. HR 파트 관리자가 팀원의 발송 실패 예약을 재시도할 수 없는 것이 의도된 설계인지 확인이 필요합니다.

6. 예약 시간 초과 취소/수정 시 예외 타입 불일치

MailService.kt L309–313 (updateMailReservation), L363–367 (cancelMailReservation):

// 예약 시간이 지난 경우 → MailReservationAlreadyProcessedException 사용
throw MailReservationAlreadyProcessedException("예약 시간이 지난 메일은 수정할 수 없습니다...")

"예약 시간이 지남"과 "이미 발송됨"은 의미상 다른 케이스인데 동일한 예외 타입을 사용하고 있습니다. 클라이언트 측에서 errorCode로 구분 가능하다면 괜찮지만, 그렇지 않다면 별도 예외 클래스로 분리하는 것이 명확합니다.


🟢 긍정적인 부분

  • 테스트 커버리지 충실: retryReservation, cancelMailReservation, updateMailReservation, getUserMailReservation 등 주요 비즈니스 케이스에 대해 성공/실패 시나리오 모두 테스트 작성 👍
  • 특권 유저 테스트 분리: DevPrivilegeTestHolder를 통해 테스트 환경에서 특권 유저 동작을 제어할 수 있도록 설계된 점이 좋습니다.
  • 로깅 충실: sendReservedMails, trySendReservation 등 스케줄러 로직에 적절한 warn/error 로깅이 잘 갖춰져 있습니다.
  • FormResponseToApplicantProcessor 오버로드: 기존 인터페이스를 유지하면서 ApplicantSyncMapping 기반 새 오버로드를 추가한 방식이 하위 호환성을 잘 유지합니다.

📝 코드 품질 코멘트

SQL 파일 관리

scripts/sql/mail-reservation-queries.sqlglen.urssu@gmail.com 이메일이 하드코딩되어 있습니다. 이 파일은 디버깅용 유틸리티 쿼리로 보이는데, 특정 이메일 값을 파라미터 플레이스홀더(-- :senderEmail)로 바꿔두는 것이 좋습니다.

미사용 메서드 확인

MailReservationReader.ktreadAllBeforeBySenderEmail (단수)와 readAllBySenderEmail (단수) 메서드가 있는데, 이번 PR에서 복수(Emails) 버전으로 대체된 것으로 보입니다. 실제로 호출하는 곳이 없다면 제거를 검토해 주세요.


@seulnan
seulnan merged commit 36fbdd9 into main Mar 10, 2026
5 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant