보안 사고 수습부터 카카오 로그인 연동까지, 발표 전 마지막 스프린트 하루 기록
SBS 풀스택 팀 프로젝트 "케어매치(CareMatch)" — 요양보호사 구인구직 매칭 플랫폼
오늘은 지난주 팀 공유 브랜치에 남겨뒀던 미완수 사항들을 정리하고, 발표 전 마지막으로 작업을 분배·진행한 날이다. 아침엔 단순히 "오늘 할 일 나누기"로 시작했는데, 중간에 보안 사고 수습, 원인을 알 수 없던 main 브랜치 빌드 깨짐 사건, 그리고 카카오 로그인 실연동까지 하루 만에 꽤 많은 일이 몰렸다. 시간순으로 기록해둔다.
지난주 share 브랜치(팀원끼리 파일 공유용 브랜치)에 정리해둔 미완수사항 문서를 기준으로 팀 작업을 다시 배분했다. 크게 세 갈래였다.
다음주 발표를 앞두고 있어서, 이번 주는 "발표에서 보여줄 수 있는가"를 기준으로 우선순위를 정했다. 목요일까지 작업을 마무리하고, 이후엔 발표 준비에 집중하기로 했다.
자격증명 유출을 처음 발견했을 때 든 생각은 "그냥 브랜치 지우면 되지 않나?"였다. 그런데 곰곰이 따져보니 이게 왜 안 되는지 명확해졌다.
git filter-repo나 BFG로 히스토리 자체를 재작성하고 강제 push까지 해야 하는데, 이게 오히려 지금 상황보다 훨씬 위험하고 번거롭다.결론은 "이미 커밋된 자격증명은 유출된 것으로 간주하고, 값 자체를 재발급하는 것"이 정석 대응이라는 것. 그래서:
작은 실수 하나가 "히스토리 지우기 vs 값 재발급하기"라는, 생각보다 큰 판단을 요구한다는 걸 다시 느꼈다.
PR을 몇 개 머지하고 나서 습관적으로 "머지 잘 됐나 확인해줘"라고 점검을 요청했는데, 뜻밖에도 main 브랜치의 프론트엔드 빌드가 깨져 있는 걸 발견했다.
원인을 추적해보니, 자격증 업로드 기능 PR을 머지하는 과정에서 main과의 병합 충돌이 제대로 해소되지 않은 채 그대로 커밋되어 있었다. <<<<<<<, =======, >>>>>>> 마커 텍스트가 파일에 그대로 남아있었고, 양쪽 브랜치의 코드가 뒤섞여서 존재하지도 않는 변수를 참조하는 등 명백한 버그 상태였다.
더 흥미로웠던 건, 자격증 업로드 UI 블록이 엉뚱한 섹션에 잘못 붙어있었다는 점이다. 원인은 두 브랜치가 갈라진 시점 차이 — 자격증 업로드 브랜치는 프로필 로딩 로직이 리팩터링되기 전 시점에서 분기했기 때문에, git의 라인 기반 diff 알고리즘이 자격증 UI 블록을 원래 있어야 할 위치가 아니라 구조가 비슷한 다른 섹션에 붙여버린 것이었다.
두 브랜치의 원본 커밋을 각각 꺼내서 3-way merge를 다시 재현해보고, 양쪽 의도를 모두 살려서 수동으로 병합했다. tsc, eslint, 프로덕션 빌드까지 통과를 확인한 뒤에야 안심할 수 있었다. "머지됐다"와 "제대로 머지됐다"는 다른 이야기라는 걸 배운 사건이었다.
지난주 문서엔 "공고에 지원하기 기능이 아직 없다"고 적혀 있었다. 그런데 막상 코드를 다시 열어보니:
POST /api/job-postings/{id}/applications 엔드포인트가 지원 생성, 중복 지원 방지, 마감 공고 차단까지 다 처리하고 있었다.즉, 예상했던 "백엔드+프론트 둘 다 새로 만들어야 하는 큰 작업"이 실제로는 "프론트 연동 하나만 하면 되는 작은 작업"으로 줄어들었다. 이럴 때 문서만 믿고 작업 분량을 예단하면 안 된다는 걸 다시 느꼈다 — 실제 코드를 열어봐야 진짜 스코프가 보인다.
관심 공고 버튼(하트 아이콘)이 이미 같은 패턴(비로그인 시 로그인 화면 이동, 실패 시 롤백)으로 구현돼 있어서, 그 구조를 그대로 재사용해 지원 버튼을 만들었다. 이미 지원한 공고는 "지원완료"로 표시하고, 구직 프로필이 아직 없는 사용자는 프로필 등록 화면으로 안내하는 흐름까지 붙였다.
팀 결정으로 연동 로그인은 카카오만 남기고 네이버/구글 버튼을 숨기기로 했다. 이용자 연령대가 높은 서비스 특성상 카카오 로그인 위주로 가는 게 맞다는 판단이었다.
버튼을 숨기는 건 배열에서 항목 두 개를 지우는 간단한 작업이었지만, 실제 카카오 로그인을 붙이는 건 또 다른 이야기였다.
카카오 로그인에서 이메일 정보를 받아오려면 "동의항목 심사"를 거쳐야 하는데, 사업자등록증이 없어서 걱정했다. 찾아보니 다행히 "비즈 앱 전환"(사업자등록 필요) 대신 "개인 개발자 본인인증"으로도 신청이 가능했다. 다만 이것도 카카오 쪽 심사에 며칠이 걸리는 절차라, 발표 전까지 승인이 날 거란 보장이 없었다.
그래서 실용적인 선택을 했다 — 이메일 동의항목은 이번엔 신청하지 않고, 닉네임만 받기로 한 것. 다행히 코드는 이미 이메일이 없는 경우를 대비해서 임시 이메일을 자동 생성하는 로직(emailOrPlaceholder())을 갖추고 있어서, 승인 대기 없이 바로 서비스를 붙일 수 있었다. 카카오 이메일 심사는 발표 이후, 여유 있을 때 다시 신청하기로 했다.
문서와 예전 기억을 바탕으로 "카카오 로그인 > 보안" 메뉴에서 Client Secret을 발급하면 된다고 안내했는데, 실제 콘솔엔 그 메뉴 자체가 없었다. 카카오가 UI를 개편해서:
스크린샷을 주고받으며 실제 화면을 보고서야 정확한 위치를 찾을 수 있었다. 문서나 기억에 의존하지 말고, 막히면 바로 스크린샷으로 확인하는 게 제일 빠르다는 걸 새삼 느꼈다.
REST API 키, Client Secret 발급 → Render 환경변수(OAUTH_KAKAO_CLIENT_ID, OAUTH_KAKAO_CLIENT_SECRET, OAUTH_REDIRECT_BASE)에 반영 → 재배포 → 실제로 카카오 계정으로 로그인까지 테스트 완료. 배포 반영에 예상보다 시간이 걸려서 몇 분간 계속 확인했는데, 결국 정상적으로 실제 카카오 인증 화면으로 넘어가는 걸 확인했다.
로컬에서 로그인 테스트를 하다가 "서버에 연결할 수 없다"는 에러를 만났다. 처음엔 혹시 오늘 건드린 Render 배포나 R2 키 작업 때문에 서버가 죽은 게 아닌가 걱정했는데, 확인해보니 원인은 훨씬 단순했다 — 로컬 프론트가 로컬 백엔드(localhost:8080)를 보고 있는데, 로컬 백엔드를 안 띄운 상태였던 것. 배포된 백엔드는 멀쩡히 잘 돌아가고 있었다.
.env.local의 API 주소를 배포 서버로 바꾸고 dev 서버를 재시작하니 바로 해결됐다. 별일 아니었지만, "에러 메시지만 보고 성급하게 원인을 짐작하지 않고 하나씩 확인해보는 습관"의 중요성을 다시 느낀 순간이었다.
| 항목 | 상태 |
|---|---|
| 보안 사고 수습 (R2 키, admin 계정, DB 직접 조치) | ✅ 완료 |
| main 브랜치 빌드 깨짐 발견 및 수정 | ✅ 완료 |
| 공고에 "지원하기" 기능 프론트 연동 | ✅ 완료 |
| 로그인 카카오 단일화 (네이버/구글 숨김) | ✅ 완료 |
| 카카오 로그인 실제 연동 (콘솔 설정 + Render 반영 + 테스트) | ✅ 완료 |
남은 건 카카오맵 연동 여부 결정, CI 파이프라인 추가 정도. 발표까지 남은 시간을 생각하면 이제부터는 새 기능보다 안정성 확인과 발표 시나리오 리허설에 무게를 둬야 할 것 같다.
하루 사이에 "단순 작업 분배"로 시작해서 "보안 사고 대응 + 숨은 버그 발견 + 외부 서비스 연동"까지 이어진 걸 보면, 계획대로만 흘러가지 않는 게 개발이라는 걸 다시 느낀다. 그래도 발표 전에 이런 것들을 미리 발견하고 고칠 수 있어서 다행이었다.