Ject 회고록

2026. 6. 22. 19:10·Review
728x90

 

 

이 활동은 젝트에서 진행한 프로젝트입니다.
젝트 공식 홈페이지 👉 https://ject.kr


젝트 4기 2팀, 첫 절반을 돌아보며 — 1차 활동 회고

들어가며

올해 초, "혼자 공부만 하다 보니 협업다운 협업을 해본 적이 없네"라는 생각이 머릿속을 떠나지 않았다.
그러던 차에 젝트(JECT)를 알게 됐고, 4기에 합류했다. 그렇게 4기 2팀의 백엔드로 한 학기 가까운 시간을 보냈다.

내가 맡은 영역은 한마디로 "사용자가 우리 서비스에 들어오고, 머물고, 떠나는 모든 과정" 이었다.
로그인부터 회원가입, 마이페이지, 그리고 회원 탈퇴까지 — 인증(Auth)과 유저(User) 도메인이 내 담당이었다.

3월 말 첫 커밋부터 6월까지, 돌아보니 어느새 98개의 커밋이 쌓여 있었다.
거창한 회고는 아니지만, 그 사이에 내가 무엇을 만들었고 무엇을 배웠는지 담담하게 적어보려 한다.

온보딩, 그리고 낯선 단어들

처음 레포를 열었을 때 가장 낯설었던 건 헥사고날 아키텍처였다.
domain, port, adapter로 폴더가 나뉘어 있었는데, 처음엔 "왜 이렇게까지 나누지?" 싶었다.
컨트롤러에서 바로 서비스를 호출하던 익숙한 방식과 달리, 도메인은 바깥 세상(웹, DB)을 몰라야 한다는 규칙이 있었다.

솔직히 초반엔 그 경계를 자주 넘나들었다. 도메인 객체가 웹 요청 DTO를 알고 있다거나,
서비스가 컨트롤러 사정을 너무 잘 알고 있다거나. 이 "경계 감각"을 익히는 게 온보딩 기간의 첫 숙제였다.

내가 만든 것들

로그인의 시작 — OAuth2와 JWT

가장 먼저 손댄 건 Google 소셜 로그인이었다.
사용자가 구글 계정으로 로그인하면, 우리 서버가 그 사람을 식별할 수 있는 토큰(JWT)을 발급해주는 구조다.

  • Access Token: 짧게 살아 있으면서 "이 사람 누구야?"를 매 요청마다 증명
  • Refresh Token: Access Token이 만료됐을 때 다시 발급받기 위한 열쇠

말로는 간단하지만, 토큰을 언제 만들고, 어떻게 검증하고, 만료되면 어떻게 재발급할지 흐름을 직접 짜보니
인증이라는 게 생각보다 섬세한 영역이라는 걸 알게 됐다. 이때 처음으로 단위 테스트(JwtProviderTest, JwtAuthFilterTest)도
제대로 써봤는데, "내가 짠 토큰 검증 로직이 진짜 맞나?"를 코드로 확인하는 경험이 꽤 든든했다.

"토큰을 대체 어디에 담지?" — 쿼리 파라미터에서 쿠키로

처음엔 토큰을 URL 쿼리 파라미터로 주고받았다. 동작은 했다. 그런데 어딘가 찜찜했다.
URL에 토큰이 그대로 노출되면 브라우저 히스토리, 서버 로그, 공유 링크 어디에든 남을 수 있었다.

그래서 토큰 전달 방식을 HttpOnly 쿠키로 통째로 바꿨다.

ResponseCookie accessTokenCookie = ResponseCookie.from("accessToken", token)
        .httpOnly(true)        // 자바스크립트로 토큰을 훔칠 수 없게
        .secure(true)          // HTTPS에서만 전송
        .sameSite("...")       // CSRF 완화
        .domain(".vs.io.kr")   // 서브도메인 간 공유
        .build();

httpOnly 하나로 프론트엔드 스크립트조차 토큰을 읽지 못하게 막을 수 있다는 걸 알았을 때,
"보안은 거창한 게 아니라 이런 작은 설정의 합이구나" 싶었다. 도메인(.vs.io.kr)·SameSite·Secure 옵션을
하나씩 맞춰가며 쿠키 한 줄에 이렇게 많은 고민이 들어간다는 걸 처음 체감했다.

닉네임에 진심이던 날들 — 추천 닉네임과 욕설 필터

회원가입 단계에선 추천 닉네임을 만들었다. "형용사 + 동물 이름(2자)" 조합으로 자동 생성해서,
사용자가 고민 없이 시작할 수 있게 했다. (데이터셋을 다듬는 커밋이 유독 많았던 건… 동물 이름 고르는 게 의외로 어려웠기 때문이다.)

그런데 닉네임을 자유롭게 바꿀 수 있게 하니, 자연스럽게 따라온 고민이 욕설 필터링이었다.
단순히 contains()로 검사할 수도 있었지만, 단어 수가 늘어날수록 비효율적이라 직접 KMP(Knuth–Morris–Pratt) 알고리즘으로
문자열 매칭기를 구현해봤다. 학교에서 배우기만 했던 알고리즘을 실제 기능에 써본 첫 경험이라 소소하게 뿌듯했다.
이후엔 대소문자를 구분하지 않도록 고치고, 영어 slang 단어까지 사전을 넓혀가며 필터를 다듬었다.

마이페이지, 그리고 떠나는 사람을 배웅하는 법

마이페이지에선 프로필 조회, 이름·이미지 편집, 그리고 내가 참여한 투표(진행 중 / 종료, 최신순·인기순)를 볼 수 있게 했다.

가장 기억에 남는 건 회원 탈퇴 기능이다. 처음엔 "그냥 데이터 지우면 되는 거 아냐?"라고 생각했다.
하지만 사용자가 남긴 투표·기록은 다른 사람들과 얽혀 있어서, 진짜로 지워버리면 곳곳에 구멍이 났다.

그래서 soft delete + 익명화 방식을 택했다. 계정은 떠나되, 남은 흔적엔 본인 정보 대신
"알 수 없음"이라는 닉네임만 남도록 했다. 탈퇴 시각(withdrawnAt)을 따로 기록해 추적도 가능하게 했다.
"떠나는 사람도 잘 배웅하는 게 서비스의 책임"이라는 걸 코드로 배운 순간이었다.

협업하며 느낀 점

혼자 공부할 땐 절대 배울 수 없던 게 PR과 코드 리뷰 문화였다.

내 코드가 develop에 합쳐지려면 PR을 올리고 팀원의 리뷰를 거쳐야 했다.
처음엔 "내 코드에 빨간 줄 그어지는" 느낌이 어색했지만, 곧 그게 가장 빠르게 성장하는 길이라는 걸 알았다.

특히 기억에 남는 건 유저 정보 수정 로직을 리팩토링하면서 받은 피드백이다.
도메인과 웹 어댑터 사이에 DTO가 마구 섞여 있던 걸, 리뷰를 통해 의존 경계를 분리하게 됐다.
온보딩 때 "왜 이렇게 나누지?" 싶었던 그 헥사고날 규칙의 이유를, 직접 리팩토링하면서 비로소 납득했다.
지식으로 아는 것과 손으로 겪어보는 건 정말 달랐다.

부족했던 점과 개선

솔직히 이 기간을 돌아보면 가장 아쉬운 건 '집중'이었다.

프로젝트 중간에 소프트웨어 마에스트로(소마) 17기에 합격하면서, 두 가지 큰 일을 동시에 끌고 가야 했다.
둘 다 욕심나는 일이었지만, 그만큼 어느 한쪽에 온전히 몰입하기가 쉽지 않았다.
시간에 쫓기다 보니 기능 구현이 늘 우선순위에 밀렸고, 결국 테스트 코드를 제대로 작성하지 못한 채
Postman으로 동작만 확인하고 push를 날린 적
이 많았다.

"일단 돌아가니까"로 넘어간 코드들은, 나중에 리팩토링하거나 디버깅할 때 부메랑이 되어 돌아오곤 했다.

또 하나 아쉬운 건 AI를 거의 활용하지 않았다는 점이다.
반복적인 부분을 AI에 맡기고 잘 분담했다면 더 간단하고 빠르게 짤 수 있었을 텐데,
"내가 직접 작성하지 않은 코드는 내 것이 아니다"라는 생각에 갇혀 도구를 멀리했다.
지금 돌아보면 모든 줄을 손으로 쳐야 내 실력이 된다는 건 일종의 강박이었던 것 같다.
정말 중요한 건 결과물을 이해하고 책임질 수 있느냐이지, 한 글자도 빠짐없이 타이핑했느냐가 아니었다.

(인프라 쪽에서도 시행착오가 많았다. 구글 OAuth 키나 쿠키 도메인 같은 민감한 값을 GitHub Actions에 주입하는 과정에서
빌드가 자주 깨졌고, 그 덕에 "코드를 짜는 것과 안전하게 돌아가게 하는 건 별개의 능력"이라는 걸 배웠다.)

다음 기간(젝러닝 ~ 데모데이)엔 두 가지를 분명히 바꾸고 싶다.
하나는 기능과 테스트를 함께 가져가는 흐름을 만드는 것, 다른 하나는 AI를 똑똑하게 활용해 본질에 더 집중하는 것이다.

마치며

숫자로 돌아보면 3월 말부터 6월까지 98개의 커밋, 약 +8,000줄.
하지만 그보다 값진 건, 로그인 화면 너머에서 일어나는 일들 — 토큰이 오가고, 쿠키가 보안을 지키고,
누군가 떠나도 그 자리를 정중히 비워두는 — 그 보이지 않는 일들을 내 손으로 만들어봤다는 경험이다.

아직 절반이다. 데모데이까지 남은 기간엔, 지금까지 쌓은 것들을 더 단단하게 다듬고
테스트와 안정성에도 더 욕심을 내보려 한다.


이 활동은 젝트에서 진행한 프로젝트입니다.
젝트 공식 홈페이지 👉 https://ject.kr

728x90
저작자표시 (새창열림)

'Review' 카테고리의 다른 글

[ByteByteGo] Database Indexing Strategies(데이터 베이스 인덱스 전략)  (0) 2026.07.12
Ject 2차 회고록  (0) 2026.07.09
[ByteByteGo] Multi-Region Architecture: Going Global Without Going Broke  (0) 2026.07.05
[ByteByteGo] How AI Agents Manage Memory and Avoid Forgetfulness  (0) 2026.07.04
'Review' 카테고리의 다른 글
  • [ByteByteGo] Database Indexing Strategies(데이터 베이스 인덱스 전략)
  • Ject 2차 회고록
  • [ByteByteGo] Multi-Region Architecture: Going Global Without Going Broke
  • [ByteByteGo] How AI Agents Manage Memory and Avoid Forgetfulness
쿨쿨.
쿨쿨.
  • 쿨쿨.
    All of the life
    쿨쿨.
  • 전체
    오늘
    어제
    • 분류 전체보기 (278)
      • Programming (47)
        • C, C++ (18)
        • Python (6)
        • Java (1)
        • HTML,CSS,JS (3)
        • SQL(DB) (13)
        • SpringBoot (3)
        • Android (2)
        • CI,CD (1)
      • Algorithm (173)
        • Review (4)
      • Security (14)
        • WebHacking (3)
        • Websecurity (11)
      • OS (19)
        • Linux (12)
        • Mac os (2)
      • 머신러닝 (1)
      • CS(Computer Science) (12)
        • 컴퓨터 네트워크 (3)
        • 컴퓨터 구조 (1)
        • 인공지능 (8)
      • Docker (1)
      • Dev Book Review (3)
        • Clean Code (1)
        • Effective Java (0)
        • Real MySQL (2)
      • SWM (1)
      • Review (5)
      • AWS (2)
  • 블로그 메뉴

    • 홈
    • 태그
    • 방명록
  • 링크

  • 공지사항

  • 인기 글

  • 태그

    이분탐색
    WebSecurity
    구현
    트리
    다익스트라
    에라토스테네스의 체
    백트래킹
    그래프 이론
    Leviathan
    BFS
    우선순위 큐
    wargame
    비트마스킹
    c언어
    linux
    DFS
    재귀
    투포인터
    시뮬레이션
    정렬
    백준
    브루트포스
    다이나믹 프로그래밍
    우선순위큐
    깊이우선탐색
    그리디
    DP
    누적합
    코딩
    Bandit
  • 최근 댓글

  • 최근 글

  • 250x250
  • hELLO· Designed By정상우.v4.10.6
쿨쿨.
Ject 회고록
상단으로

티스토리툴바