Fullstack 106

heo4·2026년 9월 16일

Fullstack

목록 보기
61/70

풀스택

Care Match

어제 글은 PR #127(결제/포인트 동시요청 이중 처리 버그 수정)까지였다. 그 뒤로 내가 직접 손댄 건 두 가지였다.

1. 회원 탈퇴 기능 (#131, #132)

회원 탈퇴는 단순히 "계정 삭제" 버튼 하나가 아니라 몇 가지를 같이 고려해야 했다:

  • 탈퇴한 계정의 로그인 세션(Refresh Token)을 무효화할 것 — 비밀번호 찾기 때 만든 세션 무효화 로직을 그대로 재사용했다.
  • 하드 삭제가 아니라 소프트 삭제 방향으로 처리 — 운영 데이터(구인공고, 지원 이력 등)와의 FK 관계를 그대로 끊지 않기 위함.

#131 feat: 회원 탈퇴 API 추가 로 백엔드를 먼저 올리고, #132 feat: 마이페이지 설정에 회원 탈퇴 버튼 추가 로 프론트까지 직접 붙였다.

2. README에 "팀원별 작업 내역" 정리 (#136, #137)

발표 이후 프로젝트를 정리하는 김에, 지금까지 4명이 각자 뭘 했는지 README에 한눈에 보이게 남겨두고 싶었다. Claude Code로 커밋 히스토리(9/7~9/16)를 전부 사람별로 묶어서 정리했다.

처음 버전(#136)은 사람마다 항목을 한 줄에 죽 나열하는 방식이었는데, 보고 나니 맥락 없이 나열만 된 느낌이라 다시 요청해서 주제별로 묶었다 — 초기 셋업 / 회원·인증 / 구인공고 / 포인트·결제 같은 소제목 아래로 항목을 다시 묶는 방식(#137).

재밌는 해프닝이 하나 있었다: 첫 번째 커밋(#136)이 이미 머지된 직후에 두 번째 커밋(재정리)을 같은 브랜치에 얹어서 푸시했는데, PR이 이미 닫힌 뒤라 그 커밋은 main에 반영이 안 되고 있었다. 결국 같은 브랜치로 PR을 하나 더 열어서(#137) 마저 반영했다 — "머지됐다고 안심하지 말고 fetch해서 확인하자"는 걸 다시 한번 느낀 순간.


3. 회원관리에 인증마크 토글 버튼 추가

정상적인 "인증구직자" 마크 경로는 자격증 심사 승인 + 경력인증 승인 + 본인 신청 + 관리자 최종승인, 총 네 단계를 거쳐야 한다. 테스트 계정 하나에 마크 달아보려고 이 과정을 매번 다 밟는 건 비효율적이라, 관리자 회원관리 화면에 "마크 활성화/해제" 버튼을 만들어 절차를 통째로 건너뛸 수 있게 했다.

PATCH /api/admin/members/{memberId}/verified-badge
{ "granted": true }

구직회원이 아닌 계정에 시도하면 400(BADGE_011)으로 막고, 응답으로는 갱신된 회원 요약을 그대로 돌려줘서 화면이 별도 리로드 없이 즉시 갱신되게 했다.

4. "머지했는데 에러나는데?" — 알고 보니 배포 지연

PR 머지하고 바로 "aaaa1"에 눌러봤다는 연락이 왔는데 에러가 난다고 했다. 코드가 잘못됐나 싶어서 다시 열어봤는데 로직상 문제는 안 보였다.

/actuator/info로 Render에 실제 배포된 커밋 해시를 찍어보니 방금 머지한 커밋보다 몇 커밋 전 상태였다. 프론트(Vercel)는 이미 새 버튼이 올라가 있었는데, 백엔드(Render)만 재배포가 안 끝나서 새로 만든 엔드포인트 자체가 없었던 것 — 그러니 버튼을 눌러도 404로 실패할 수밖에.

잠깐 기다렸다가 /actuator/info를 다시 찍어보니 최신 커밋으로 바뀌어 있었고, 그 뒤로는 정상 동작했다. 코드 버그가 아니라 단순 배포 타이밍 문제였다 — 그래도 "머지 직후엔 실제 배포가 끝났는지 확인부터 하자"는 걸 다시 배웠다.

5. 구인공고 정렬에 매칭점수순 추가

이어서 나온 요청: "구인공고 정렬에 매칭점수순도 있으면 좋겠다." 기존 추천순(RECOMMENDED)은 유료 노출등급이 1순위고 매칭점수는 같은 등급 안에서만(그것도 페이지 단위 근사치로) 반영되는 구조였는데, 이번엔 등급과 무관하게 순수 매칭점수로만 줄 세우는 별도 옵션을 만들었다.

문제는 매칭점수가 DB 컬럼이 아니라 조회 시점에 계산되는 값이라 SQL ORDER BY로 정렬할 수 없다는 것. 조건에 맞는 공고를 전부(최대 1000건 안전상한) 메모리에 올려서 점수를 매기고, 정렬한 다음, 요청받은 페이지만큼 잘라내는 방식으로 구현했다.

6. "근데 등급 상관없이 점수가 1순위여야 하는데?" — 진짜 버그 하나 더

만들어서 올렸더니 팀원이 다시 짚었다: "매칭점수순인데 등급 상관없이 점수가 1순위여야 하는 거 아니야?" 코드를 다시 보니 실제로 구멍이 있었다.

if ("MATCH_SCORE".equals(sortKey) && viewer != null) {
    return searchByMatchScore(...);
}
// viewer == null 이면 여기로 떨어져서
// resolveSort() 의 default 분기(exposurePriority 우선)를 타 버린다

비로그인 상태거나 구직회원이 아닌 계정(관리자로 테스트하는 경우 포함)으로 매칭점수순을 선택하면, "점수 매길 대상이 없다"는 이유로 조용히 기존 추천순 로직(노출등급 우선)으로 빠지고 있었다. 그러니 관리자 계정으로 테스트하면 매칭점수순을 골라도 여전히 유료 노출 공고가 위에 뜨는 것처럼 보였을 것이다.

viewer 유무와 무관하게 항상 매칭점수 전용 정렬 경로를 타도록 고쳤다. 대상 점수가 없으면(비로그인 등) 전원 무점수로 묶여 결과적으로 최신순과 같아지긴 하지만, 노출등급이 순서에 끼어드는 일은 어떤 경우에도 없게 만들었다.

7. 그 사이 팀원들도

동한씨가 경력인증에 증빙 파일 첨부 기능(텍스트만 받던 걸 실제 서류 업로드로)을 붙였고, 구직신청 등록이 "요청 값이 올바르지 않습니다"로 실패하던 버그도 같이 고쳤다.


오늘 올린 PR

PR내용
#131회원 탈퇴 API 추가
#132마이페이지 설정에 회원 탈퇴 버튼 추가
#136, #137README에 팀원별 작업 내역(주제별 그룹) 추가
#147관리자 회원관리에서 인증구직자 마크 수동 부여/해제
#148구인공고 정렬에 매칭점수순 추가
#150매칭점수순이 비구직자에겐 노출등급 우선으로 되돌아가던 버그 수정
#152README에 오늘 작업분 반영

"이제 끝났다" 싶을 때마다 한 걸음씩 더 나간 하루였다.

0개의 댓글