Fullstack 104

heo4·2026년 9월 13일

Fullstack

목록 보기
59/70

풀스택

CareMatch 개발일지

보안 사고 수습부터 카카오 로그인 연동까지, 발표 전 마지막 스프린트 하루 기록

SBS 풀스택 팀 프로젝트 "케어매치(CareMatch)" — 요양보호사 구인구직 매칭 플랫폼

들어가며

오늘은 지난주 팀 공유 브랜치에 남겨뒀던 미완수 사항들을 정리하고, 발표 전 마지막으로 작업을 분배·진행한 날이다. 아침엔 단순히 "오늘 할 일 나누기"로 시작했는데, 중간에 보안 사고 수습, 원인을 알 수 없던 main 브랜치 빌드 깨짐 사건, 그리고 카카오 로그인 실연동까지 하루 만에 꽤 많은 일이 몰렸다. 시간순으로 기록해둔다.


1. 아침: 지난주 미완수 사항 정리 및 작업 분배

지난주 share 브랜치(팀원끼리 파일 공유용 브랜치)에 정리해둔 미완수사항 문서를 기준으로 팀 작업을 다시 배분했다. 크게 세 갈래였다.

  • 보안 이슈 — 실수로 공유 브랜치에 자격증명(구글 계정 비밀번호, admin 비밀번호, R2 Access/Secret Key)을 평문으로 올렸던 것을 뒤늦게 발견. 파일 내용 자체는 지웠지만 git 히스토리엔 그대로 남아있어 재발급이 필요한 상태였다.
  • 프론트-백엔드 연동 격차 — 인재정보 연동, 구직 프로필 저장 등은 이미 끝났고, 남은 건 "공고에 지원하기" 기능 하나였다.
  • 신규 기능 로드맵 — 안심번호, 신뢰도 등급, 결제 연동(PostOne) 등은 이번 스프린트 범위 밖으로 판단하고 보류.

다음주 발표를 앞두고 있어서, 이번 주는 "발표에서 보여줄 수 있는가"를 기준으로 우선순위를 정했다. 목요일까지 작업을 마무리하고, 이후엔 발표 준비에 집중하기로 했다.


2. 보안 사고 수습 — "브랜치 지우면 되는 거 아니야?"라는 생각의 함정

자격증명 유출을 처음 발견했을 때 든 생각은 "그냥 브랜치 지우면 되지 않나?"였다. 그런데 곰곰이 따져보니 이게 왜 안 되는지 명확해졌다.

  • git 브랜치를 삭제해도 이미 pull 받은 팀원들의 로컬 저장소엔 히스토리가 그대로 남는다.
  • 원격 브랜치 삭제는 레퍼런스만 지우는 것이라, GitHub 쪽에 커밋 객체 자체는 일정 기간 dangling 상태로 남을 수 있다.
  • 완전히 지우려면 git filter-repo나 BFG로 히스토리 자체를 재작성하고 강제 push까지 해야 하는데, 이게 오히려 지금 상황보다 훨씬 위험하고 번거롭다.

결론은 "이미 커밋된 자격증명은 유출된 것으로 간주하고, 값 자체를 재발급하는 것"이 정석 대응이라는 것. 그래서:

  1. Cloudflare R2 Access/Secret Key 재발급 → Render 환경변수 갱신 → 예전 키는 Cloudflare에서 폐기
  2. admin 계정 — 비밀번호 변경 API가 없어서(원래 관리 화면에서 바꾸는 기능 자체가 없었음), DB에 직접 BCrypt 해시로 UPDATE 쿼리를 날려서 처리. 발표용으로 쓸 새 admin 계정도 별도로 하나 더 만들었다.
  3. 구글 계정 — 프로젝트 전용 임시 계정이라 프로젝트 종료 후 폐기 예정이라는 걸 확인하고, 다른 서비스 로그인에 연결돼 있지 않은 걸 확인한 뒤 이번엔 스킵.

작은 실수 하나가 "히스토리 지우기 vs 값 재발급하기"라는, 생각보다 큰 판단을 요구한다는 걸 다시 느꼈다.


3. 원인불명의 main 브랜치 빌드 깨짐 — 병합 충돌의 흔적

PR을 몇 개 머지하고 나서 습관적으로 "머지 잘 됐나 확인해줘"라고 점검을 요청했는데, 뜻밖에도 main 브랜치의 프론트엔드 빌드가 깨져 있는 걸 발견했다.

원인을 추적해보니, 자격증 업로드 기능 PR을 머지하는 과정에서 main과의 병합 충돌이 제대로 해소되지 않은 채 그대로 커밋되어 있었다. <<<<<<<, =======, >>>>>>> 마커 텍스트가 파일에 그대로 남아있었고, 양쪽 브랜치의 코드가 뒤섞여서 존재하지도 않는 변수를 참조하는 등 명백한 버그 상태였다.

더 흥미로웠던 건, 자격증 업로드 UI 블록이 엉뚱한 섹션에 잘못 붙어있었다는 점이다. 원인은 두 브랜치가 갈라진 시점 차이 — 자격증 업로드 브랜치는 프로필 로딩 로직이 리팩터링되기 전 시점에서 분기했기 때문에, git의 라인 기반 diff 알고리즘이 자격증 UI 블록을 원래 있어야 할 위치가 아니라 구조가 비슷한 다른 섹션에 붙여버린 것이었다.

두 브랜치의 원본 커밋을 각각 꺼내서 3-way merge를 다시 재현해보고, 양쪽 의도를 모두 살려서 수동으로 병합했다. tsc, eslint, 프로덕션 빌드까지 통과를 확인한 뒤에야 안심할 수 있었다. "머지됐다"와 "제대로 머지됐다"는 다른 이야기라는 걸 배운 사건이었다.


4. "공고에 지원하기" — 기능이 반은 이미 있었다

지난주 문서엔 "공고에 지원하기 기능이 아직 없다"고 적혀 있었다. 그런데 막상 코드를 다시 열어보니:

  • 백엔드는 이미 완성돼 있었다. POST /api/job-postings/{id}/applications 엔드포인트가 지원 생성, 중복 지원 방지, 마감 공고 차단까지 다 처리하고 있었다.
  • 프론트엔드만 이 API를 안 쓰고 있었다. 공고 상세 페이지의 "온라인으로 지원하기" 버튼 두 곳 모두, 실제 지원 처리 없이 그냥 프로필 등록 화면으로 이동만 시키고 있었다.

즉, 예상했던 "백엔드+프론트 둘 다 새로 만들어야 하는 큰 작업"이 실제로는 "프론트 연동 하나만 하면 되는 작은 작업"으로 줄어들었다. 이럴 때 문서만 믿고 작업 분량을 예단하면 안 된다는 걸 다시 느꼈다 — 실제 코드를 열어봐야 진짜 스코프가 보인다.

관심 공고 버튼(하트 아이콘)이 이미 같은 패턴(비로그인 시 로그인 화면 이동, 실패 시 롤백)으로 구현돼 있어서, 그 구조를 그대로 재사용해 지원 버튼을 만들었다. 이미 지원한 공고는 "지원완료"로 표시하고, 구직 프로필이 아직 없는 사용자는 프로필 등록 화면으로 안내하는 흐름까지 붙였다.


5. 로그인은 카카오만 — 그리고 실제 연동까지

팀 결정으로 연동 로그인은 카카오만 남기고 네이버/구글 버튼을 숨기기로 했다. 이용자 연령대가 높은 서비스 특성상 카카오 로그인 위주로 가는 게 맞다는 판단이었다.

버튼을 숨기는 건 배열에서 항목 두 개를 지우는 간단한 작업이었지만, 실제 카카오 로그인을 붙이는 건 또 다른 이야기였다.

사업자등록증이 없어도 될까?

카카오 로그인에서 이메일 정보를 받아오려면 "동의항목 심사"를 거쳐야 하는데, 사업자등록증이 없어서 걱정했다. 찾아보니 다행히 "비즈 앱 전환"(사업자등록 필요) 대신 "개인 개발자 본인인증"으로도 신청이 가능했다. 다만 이것도 카카오 쪽 심사에 며칠이 걸리는 절차라, 발표 전까지 승인이 날 거란 보장이 없었다.

그래서 실용적인 선택을 했다 — 이메일 동의항목은 이번엔 신청하지 않고, 닉네임만 받기로 한 것. 다행히 코드는 이미 이메일이 없는 경우를 대비해서 임시 이메일을 자동 생성하는 로직(emailOrPlaceholder())을 갖추고 있어서, 승인 대기 없이 바로 서비스를 붙일 수 있었다. 카카오 이메일 심사는 발표 이후, 여유 있을 때 다시 신청하기로 했다.

카카오 콘솔 UI가 바뀌어 있었다

문서와 예전 기억을 바탕으로 "카카오 로그인 > 보안" 메뉴에서 Client Secret을 발급하면 된다고 안내했는데, 실제 콘솔엔 그 메뉴 자체가 없었다. 카카오가 UI를 개편해서:

  • Redirect URI 등록 위치가 "카카오 로그인" 제품 설정에서 "앱 > 플랫폼 키 > REST API 키 카드 안"으로 이동해 있었다.
  • "카카오 로그인 > 고급" 메뉴엔 로그인용이 아니라 로그아웃 리다이렉트 URI만 있어서 처음엔 헷갈렸다.

스크린샷을 주고받으며 실제 화면을 보고서야 정확한 위치를 찾을 수 있었다. 문서나 기억에 의존하지 말고, 막히면 바로 스크린샷으로 확인하는 게 제일 빠르다는 걸 새삼 느꼈다.

결과

REST API 키, Client Secret 발급 → Render 환경변수(OAUTH_KAKAO_CLIENT_ID, OAUTH_KAKAO_CLIENT_SECRET, OAUTH_REDIRECT_BASE)에 반영 → 재배포 → 실제로 카카오 계정으로 로그인까지 테스트 완료. 배포 반영에 예상보다 시간이 걸려서 몇 분간 계속 확인했는데, 결국 정상적으로 실제 카카오 인증 화면으로 넘어가는 걸 확인했다.


6. 사소하지만 헷갈렸던 것 — "서버에 연결할 수 없습니다"

로컬에서 로그인 테스트를 하다가 "서버에 연결할 수 없다"는 에러를 만났다. 처음엔 혹시 오늘 건드린 Render 배포나 R2 키 작업 때문에 서버가 죽은 게 아닌가 걱정했는데, 확인해보니 원인은 훨씬 단순했다 — 로컬 프론트가 로컬 백엔드(localhost:8080)를 보고 있는데, 로컬 백엔드를 안 띄운 상태였던 것. 배포된 백엔드는 멀쩡히 잘 돌아가고 있었다.

.env.local의 API 주소를 배포 서버로 바꾸고 dev 서버를 재시작하니 바로 해결됐다. 별일 아니었지만, "에러 메시지만 보고 성급하게 원인을 짐작하지 않고 하나씩 확인해보는 습관"의 중요성을 다시 느낀 순간이었다.


오늘의 정리

항목상태
보안 사고 수습 (R2 키, admin 계정, DB 직접 조치)✅ 완료
main 브랜치 빌드 깨짐 발견 및 수정✅ 완료
공고에 "지원하기" 기능 프론트 연동✅ 완료
로그인 카카오 단일화 (네이버/구글 숨김)✅ 완료
카카오 로그인 실제 연동 (콘솔 설정 + Render 반영 + 테스트)✅ 완료

남은 건 카카오맵 연동 여부 결정, CI 파이프라인 추가 정도. 발표까지 남은 시간을 생각하면 이제부터는 새 기능보다 안정성 확인과 발표 시나리오 리허설에 무게를 둬야 할 것 같다.

하루 사이에 "단순 작업 분배"로 시작해서 "보안 사고 대응 + 숨은 버그 발견 + 외부 서비스 연동"까지 이어진 걸 보면, 계획대로만 흘러가지 않는 게 개발이라는 걸 다시 느낀다. 그래도 발표 전에 이런 것들을 미리 발견하고 고칠 수 있어서 다행이었다.

0개의 댓글