"코드베이스를 이해하는 가장 빠른 길은 처음부터 읽는 것이 아니다. 그것을 살아 움직이게 만든 다음, 하나의 길을 끝까지 따라가는 것이다."
프롤로그 — 우리는 반복해서 새로 들어온 사람이 된다
"적응(ramp-up)"이라는 단어를 들으면 신입 개발자의 첫 출근날을 떠올리기 쉽습니다. 하지만 현실은 다릅니다. 우리는 커리어 내내 반복해서 새로 들어온 사람이 됩니다.
이직을 하면 새 회사의 코드베이스 앞에 섭니다. 팀을 옮기면 같은 회사인데도 처음 보는 서비스를 맡습니다. 누군가 퇴사하면서 5년 된 결제 시스템을 인수받기도 하고, 오픈소스에 기여하려고 남의 레포를 클론하기도 합니다. 사내에서 다른 팀의 버그를 고쳐주러 잠깐 들어가기도 합니다.
이 모든 상황에 공통점이 하나 있습니다. 이미 존재하는, 내가 만들지 않은, 머릿속에 지도가 없는 코드 앞에 서 있다는 것입니다.
여기서 중요한 사실: 적응은 재능이 아닙니다. 배울 수 있는 기술입니다. 어떤 사람은 낯선 레포에서 사흘 만에 첫 PR을 올리고, 어떤 사람은 3주가 지나도 "아직 파악 중"입니다. 그 차이는 머리가 좋고 나쁨이 아니라 방법을 아느냐 모르느냐입니다.
이 글은 그 방법에 대한 것입니다. 팀이나 매니저 입장에서 "온보딩을 어떻게 설계할까"가 아니라, **개인 입장에서 "나는 어떻게 빨리 생산성을 낼까"**에 대한 실전 가이드입니다. 둘은 다릅니다. 온보딩 설계는 팀의 일이지만, 적응은 결국 내가 하는 일입니다. 좋은 온보딩 문서가 없어도 — 대부분의 경우 없습니다 — 빠르게 적응하는 기술은 내 손에 있습니다.
이 글에서 다룰 내용:
| 장 | 주제 |
|---|---|
| 1장 | 일단 돌려보기 — 가장 레버리지가 큰 한 수 |
| 2장 | 진입점 찾기 — 요청은 어디서 시작되는가 |
| 3장 | 디테일보다 형태 먼저 읽기 |
| 4장 | 요청 하나를 끝까지 따라가기 (수직 슬라이스) |
| 5장 | 테스트를 문서로 읽기 |
| 6장 | 도구를 쓴다 — grep, 정의로 이동, git을 고고학으로 |
| 7장 | 작은 변경을 일찍 한다 |
| 8장 | 멘탈 맵을 그리고 적어둔다 |
| 9장 | 무엇을, 누구에게 물을 것인가 |
| 10장 | AI 에이전트와 함께 적응하기 |
| 에필로그 | 체크리스트 + 안티패턴 + 다음 글 예고 |
1장 · 일단 돌려보기
낯선 레포를 받았을 때 첫 본능은 보통 "코드를 읽자"입니다. 잘못된 본능입니다. 가장 레버리지가 큰 첫 수는 로컬에서 돌아가게 만드는 것입니다.
1.1 작동하는 환경이 일주일치 독서를 이긴다
코드만 읽으면 머릿속에 정적인 그림만 생깁니다. 하지만 돌아가는 환경이 있으면 살아 있는 시스템을 손에 쥐게 됩니다. 값을 바꿔보고, 로그를 찍어보고, 디버거를 붙이고, "이걸 지우면 어떻게 되지?"를 실제로 해볼 수 있습니다. 읽기만 해서 사흘 걸릴 이해가, 돌려보면 한 시간에 끝나기도 합니다.
그리고 환경을 세팅하는 과정 자체가 학습입니다. 빌드가 어떻게 도는지, 어떤 서비스에 의존하는지, 환경 변수가 무엇을 켜고 끄는지 — README가 알려주지 않는 것들을 손으로 배우게 됩니다.
1.2 첫날의 목표는 "녹색 화면"
첫날 목표를 명확히 하세요. 기능을 이해하는 게 아니라, 앱이 켜지고, 테스트가 통과하는 것입니다.
git clone <repository-url>
cd project
# README의 셋업 단계를 그대로 따라간다
make setup # 또는 npm install / poetry install / ...
make dev # 앱을 띄운다
make test # 테스트가 도는지 본다
이 과정에서 막히는 게 정상입니다. 막히는 지점이야말로 첫 번째 발견입니다. 누락된 환경 변수, 깔리지 않는 의존성, 문서에 없는 사전 조건 — 이것들을 기록해 두세요. 9장에서 다루겠지만, 이건 나중에 온보딩 문서를 고칠 좋은 재료이고, 동시에 "이 팀의 셋업이 얼마나 손에 익었는가"를 보여주는 신호입니다.
1.3 막혔을 때의 순서
| 순서 | 행동 | 시간 상자 |
|---|---|---|
| 1 | README, CONTRIBUTING, docs/ 다시 읽기 | 15분 |
| 2 | 에러 메시지를 그대로 레포 내부에서 검색 | 10분 |
| 3 | git log로 최근 셋업 관련 커밋 확인 | 10분 |
| 4 | CI 설정 파일을 본다 (정답이 거기 있다) | 15분 |
| 5 | 팀 채팅에서 같은 에러 검색 | 10분 |
| 6 | 사람에게 묻는다 (정리된 질문으로) | — |
4번이 핵심입니다. CI 설정(.github/workflows/, .gitlab-ci.yml 등)은 "이 프로젝트를 깨끗한 환경에서 어떻게 세우는가"의 정답지입니다. 사람의 README는 낡지만, CI는 매번 실행되므로 거짓말을 하지 않습니다.
첫날에 "녹색 화면"을 보면, 그날부터 모든 학습이 실험이 됩니다. 못 보면, 모든 학습이 추측으로 남습니다.
2장 · 진입점 찾기
환경이 돌아가면, 다음 질문은 "이 시스템은 어디서 시작되는가"입니다. 코드베이스에는 항상 **진입점(entry point)**이 있습니다. 그곳을 찾으면 나머지가 거기서 뻗어 나갑니다.
2.1 진입점의 종류
| 시스템 유형 | 진입점 |
|---|---|
| 웹 백엔드 | main, 서버 부트스트랩, 라우트 등록 |
| 웹 프런트엔드 | 앱 루트 컴포넌트, 라우터 설정 |
| CLI 도구 | main 함수, 인자 파서 |
| 라이브러리 | public API, index 파일의 export |
| 배치/워커 | 스케줄러 등록, 큐 컨슈머 |
대부분의 프레임워크는 진입점이 어디인지 정해져 있습니다. package.json의 scripts, Makefile의 타깃, Procfile, Dockerfile의 CMD — 이것들이 "이 프로젝트가 무엇으로 시작하는가"를 알려줍니다.
2.2 요청의 일생
웹 서비스라면 가장 먼저 그려야 할 그림이 **요청의 일생(request lifecycle)**입니다. HTTP 요청 하나가 들어와서 응답이 나가기까지 어떤 단계를 거치는가.
요청의 일생 (전형적인 웹 백엔드)
HTTP 요청
│
▼
[라우터] ── URL을 핸들러에 매핑
│
▼
[미들웨어] ── 인증, 로깅, 파싱, rate limit
│
▼
[핸들러/컨트롤러] ── 요청별 진입 함수
│
▼
[서비스/도메인 계층] ── 비즈니스 로직
│
▼
[데이터 접근 계층] ── DB, 캐시, 외부 API
│
▼
[응답 직렬화] ── 결과를 JSON 등으로
│
▼
HTTP 응답
이 그림은 거의 모든 웹 백엔드에 적용됩니다. 프레임워크마다 이름과 경계가 다를 뿐입니다. 이 그림을 손에 들고 "각 계층은 이 레포에서 어느 디렉터리에 있지?"를 채워나가면, 그게 곧 코드베이스 지도의 뼈대가 됩니다.
2.3 빌드도 진입점이다
런타임 진입점만큼 중요한 게 빌드 진입점입니다. 소스에서 배포 가능한 산출물까지 어떻게 가는가. 빌드 설정 파일(vite.config, webpack.config, tsconfig, Makefile)을 한 번 훑으면, "이 코드가 실제로 어떤 형태로 실행되는가"가 보입니다. 트랜스파일되는가, 번들되는가, 어떤 환경을 타깃하는가.
3장 · 디테일보다 형태 먼저 읽기
진입점을 찾았다고 바로 함수 내부로 파고들면 안 됩니다. 먼저 **형태(shape)**를 봐야 합니다. 숲을 보고 나무로 가는 것입니다.
3.1 디렉터리 구조 = 팀의 멘탈 모델
디렉터리 구조는 단순한 폴더 정리가 아닙니다. 이 팀이 시스템을 어떻게 쪼개서 생각하는가의 화석입니다.
src/
routes/ ← HTTP 경계 (진입점)
services/ ← 비즈니스 로직
models/ ← 데이터 모양
lib/ ← 공용 유틸
jobs/ ← 백그라운드 작업
config/ ← 설정
이 구조 하나만 봐도 "이 팀은 계층형으로 생각하는구나"를 알 수 있습니다. 반대로 features/checkout/, features/search/처럼 기능별로 쪼개져 있으면 "기능 중심으로 생각하는구나"가 됩니다. 디렉터리 구조를 5분 보는 것이, 무작위 파일을 한 시간 읽는 것보다 낫습니다.
3.2 의존성 그래프 — 무엇이 무엇에 기대는가
다음으로 의존성을 봅니다. 두 가지 층위가 있습니다.
- 외부 의존성:
package.json,requirements.txt,go.mod— 어떤 프레임워크와 라이브러리 위에 서 있는가. 익숙하지 않은 게 있으면 표시해 둡니다. - 내부 의존성: 모듈끼리 어떻게 import 하는가. 어느 모듈이 "허브"인지 (많은 곳에서 import됨), 어느 모듈이 "잎"인지 (아무도 import 안 함).
허브 모듈은 시스템의 심장입니다. 거기서부터 읽으면 가장 많은 맥락을 한 번에 얻습니다.
3.3 데이터 모델이 진실을 말한다
마지막으로, 그리고 가장 중요하게 — 데이터 모델을 봅니다. 스키마 정의, ORM 모델, 마이그레이션 파일, 타입 정의.
코드는 거짓말을 할 수 있습니다. 주석은 낡습니다. 하지만 데이터 모델은 시스템이 실제로 무엇을 다루는지를 보여줍니다. User, Order, Subscription 같은 핵심 엔티티와 그들의 관계를 파악하면, 비즈니스의 절반을 이해한 것입니다. 코드가 하는 일은 결국 이 데이터를 만들고, 읽고, 바꾸고, 지우는 것이기 때문입니다.
형태를 먼저 읽으면, 나중에 디테일을 읽을 때 "이게 어디에 속하는지" 바로 압니다. 형태 없이 디테일부터 읽으면, 모든 파일이 둥둥 떠다닙니다.
4장 · 요청 하나를 끝까지 따라가기
형태를 봤다면, 이제 하나의 진짜 요청을 끝까지 따라갑니다. 이걸 수직 슬라이스(vertical slice) 기법이라고 부릅니다.
4.1 수직 슬라이스란
코드베이스 전체를 수평으로 (계층별로) 다 읽으려 하지 마세요. 대신 하나의 기능을 골라, 그것이 시스템의 모든 계층을 관통하는 길을 따라갑니다. 가장 위(요청 진입)에서 가장 아래(DB)까지, 그리고 다시 응답까지.
예: "사용자가 로그인하면 무슨 일이 일어나는가"
수직 슬라이스: 로그인 요청
POST /login ← routes/auth.ts 에서 시작
│
▼
authMiddleware 통과 ← middleware/ — 이건 건너뛰는 경로구나
│
▼
loginHandler 호출 ← controllers/auth.ts
│
▼
AuthService.login() ← services/auth.ts — 핵심 로직 여기
│
├─▶ UserRepo.findByEmail() ← db/users.ts — 쿼리는 여기서
│
├─▶ password.verify() ← lib/crypto.ts
│
└─▶ Session.create() ← services/session.ts
│
▼
Set-Cookie + 200 응답 ← 직렬화는 어디서?
이 슬라이스 하나를 따라가면, 라우팅·미들웨어·컨트롤러·서비스·리포지토리·유틸이 어떻게 연결되는지를 한 번에 체험하게 됩니다. 그리고 이 패턴은 다른 기능에도 거의 그대로 반복됩니다. 하나를 깊게 따라가면 열을 얕게 훑은 것보다 낫습니다.
4.2 슬라이스를 따라가는 도구
- 디버거 중단점을 진입점에 찍고, 진짜 요청을 한 번 보냅니다. 스택을 한 단계씩 따라가면 호출 순서가 그대로 보입니다.
- 로그 추가: 각 계층에 임시 로그를 박고 요청을 보냅니다. 어떤 순서로 찍히는지가 곧 흐름입니다. (끝나면 지웁니다.)
- **"정의로 이동"**을 연쇄적으로: 진입 함수에서 호출하는 함수로, 또 그 함수가 호출하는 함수로 계속 점프합니다.
4.3 좋은 첫 슬라이스 고르기
아무 기능이나 고르지 말고, 단순하지만 대표성 있는 것을 고르세요. 로그인, 항목 하나 조회, 간단한 폼 제출 — 시스템의 주요 계층을 다 거치지만 엣지 케이스가 적은 것. "결제 정산"이나 "리포트 생성" 같은 복잡한 것은 두 번째, 세 번째로 미룹니다.
5장 · 테스트를 문서로 읽기
테스트는 "통과/실패"를 확인하는 도구이기만 한 게 아닙니다. 가장 정직한 문서입니다.
5.1 테스트가 문서인 이유
- 주석은 낡지만, 테스트는 낡으면 빨개집니다. CI가 통과하는 한, 테스트는 현재의 동작을 정확히 반영합니다.
- 테스트는 "이 함수를 어떻게 호출하는가"의 실사용 예제입니다. 입력 형태, 기대 출력, 에러 케이스가 다 거기 있습니다.
- 테스트 이름은 종종 의도를 말합니다.
it("쿠폰이 만료되면 할인을 적용하지 않는다")— 이 한 줄이 비즈니스 규칙입니다.
5.2 테스트를 읽는 순서
1. 테스트 파일 목록을 본다
→ "이 시스템에서 무엇이 테스트할 가치가 있는가"의 지도
2. 핵심 엔티티의 단위 테스트를 읽는다
→ User, Order 등이 어떻게 동작하도록 의도됐는가
3. 통합/E2E 테스트를 읽는다
→ 계층들이 어떻게 함께 동작하도록 의도됐는가
→ 4장의 "수직 슬라이스"가 코드로 적혀 있는 경우가 많다
4. 테스트 픽스처/팩토리를 본다
→ 유효한 데이터가 실제로 어떻게 생겼는지
3번이 특히 좋습니다. 잘 쓰인 통합 테스트는 "이 기능을 쓰는 올바른 방법"을 코드로 보여줍니다. 4장에서 직접 따라간 슬라이스가 통합 테스트로 이미 적혀 있다면, 그건 검증된 지도입니다.
5.3 테스트가 없다면
테스트가 빈약한 레포도 많습니다. 그럴 땐 두 가지를 합니다. 첫째, 있는 테스트는 더 귀하게 읽습니다 — 적을수록 그게 "팀이 가장 무서워하는 부분"일 확률이 높습니다. 둘째, 7장에서 다루겠지만, 내가 첫 테스트를 추가하는 것이 좋은 첫 기여가 됩니다.
6장 · 도구를 쓴다
낯선 코드베이스에서 맨눈으로 파일을 뒤지는 건 비효율적입니다. 도구가 있습니다. 잘 쓰면 탐색 속도가 한 자릿수 배수로 빨라집니다.
6.1 검색 — grep / ripgrep
문자열 검색은 가장 기본이면서 가장 강력합니다.
# 함수/심볼이 정의되고 사용되는 모든 곳
rg "createOrder"
# 라우트가 어디서 등록되는지
rg "router\.(get|post|put|delete)"
# 환경 변수가 어디서 읽히는지
rg "process\.env\.|os\.environ"
# TODO/FIXME — 팀이 알면서 미뤄둔 것들
rg "TODO|FIXME|HACK|XXX"
# 특정 에러 메시지의 출처
rg "User not found"
ripgrep은 .gitignore를 존중하고 빠릅니다. "이 문자열이 어디서 오지?"라는 질문의 90%는 rg 한 줄로 답이 나옵니다.
6.2 정의로 이동 / 참조 찾기 / 호출 계층
IDE나 LSP가 주는 기능들입니다.
| 기능 | 답하는 질문 |
|---|---|
| 정의로 이동 | 이건 어디서 정의됐나? |
| 참조 찾기 | 이건 누가 쓰나? |
| 호출 계층 | 이 함수까지 어떤 경로로 도달하나? |
| 타입 정보 | 이 변수의 모양은? |
특히 **호출 계층(call hierarchy)**은 4장의 수직 슬라이스를 거꾸로 추적할 때 강력합니다. "이 DB 쿼리 함수는 결국 어떤 HTTP 엔드포인트들에서 호출되지?"를 한 번에 보여줍니다.
6.3 git을 고고학으로
git 히스토리는 "이 코드가 왜 이렇게 됐는가"의 기록입니다. 코드를 읽는 것은 현재를 보는 것이고, git을 읽는 것은 과거를 보는 것입니다.
# 이 줄을 마지막으로 바꾼 커밋과 그 메시지
git blame path/to/file.ts
# 이 파일의 변경 역사
git log --oneline -- path/to/file.ts
# 이 파일이 왜 이렇게 됐는지, 커밋 메시지와 함께
git log -p -- path/to/file.ts
# 특정 문자열이 추가/삭제된 커밋 찾기
git log -S "featureFlag" --oneline
# 이 함수의 변경 역사만
git log -L :functionName:path/to/file.ts
git blame으로 이상한 코드의 커밋을 찾고, 그 커밋 메시지나 연결된 PR/이슈를 읽으면 "왜 이렇게 짰는지"가 나옵니다. 이상하게 보이는 코드의 절반은 사라진 맥락 때문입니다 — 과거의 버그 수정, 외부 API의 제약, 급한 핫픽스. git은 그 맥락을 되살려 줍니다.
코드는 "무엇"을, git 히스토리는 "왜"를, 테스트는 "어떻게 써야 하는지"를 말합니다. 셋을 같이 읽으세요.
7장 · 작은 변경을 일찍 한다
읽기만 하면 적응이 끝나지 않습니다. 어느 시점에 직접 변경을 가해봐야 합니다. 그것도 가능한 한 빨리.
7.1 왜 작은 변경이 중요한가
작은 변경 하나를 끝까지 밀어보면, 개발 루프 전체가 도는지를 증명하게 됩니다. 코드 수정 → 로컬 확인 → 테스트 → 커밋 → PR → 리뷰 → CI → 머지. 이 루프가 막힘없이 도는 걸 한 번 경험하면, 그 다음 진짜 작업은 훨씬 두렵지 않습니다.
그리고 작은 변경은 빨리 피드백을 받습니다. 리뷰어가 "우리 팀은 이렇게 안 해요"라고 말해줄 기회를 일찍 만드는 것입니다. 큰 기능을 2주 만들고 나서 그 말을 듣는 것보다, 오타 수정에서 듣는 게 백 배 낫습니다.
7.2 좋은 첫 변경의 후보
| 후보 | 왜 좋은가 |
|---|---|
| 오타/문서 수정 | 위험 없이 루프 전체를 돈다 |
| 누락된 테스트 추가 | 코드를 깊이 이해했다는 증거 |
| 에러 메시지 개선 | 작지만 실사용에 도움 |
| 작은 리팩터 (이름 바꾸기) | 도구 사용 연습 + 안전 |
good first issue 라벨 | 팀이 "초심자용"으로 골라둔 것 |
good first issue나 그에 준하는 라벨이 있으면 그것부터. 없다면, 1장에서 셋업하며 발견한 문제(낡은 README 한 줄 등)가 완벽한 첫 PR감입니다.
7.3 첫 PR에서 신경 쓸 것
- 작게: 리뷰어가 5분 안에 다 읽을 수 있게.
- 팀 컨벤션 따르기: 기존 커밋 메시지, 기존 PR 설명, 기존 코드 스타일을 먼저 보고 흉내 냅니다.
- "왜"를 적기: PR 설명에 무엇을 왜 바꿨는지 한 문단.
- CI를 직접 확인: 머지를 기다리지 말고, CI가 초록인지 내가 먼저 봅니다.
첫 주에 머지된 작은 PR 하나는, 읽기만 한 첫 주보다 당신을 훨씬 멀리 데려갑니다.
8장 · 멘탈 맵을 그리고 적어둔다
지금까지 모은 것 — 진입점, 형태, 슬라이스, 테스트, git에서 캔 맥락 — 이걸 머릿속에만 두면 샙니다. 밖으로 꺼내 적어야 합니다.
8.1 자기만의 노트
거창할 필요 없습니다. 마크다운 파일 하나, 또는 위키 페이지 하나면 됩니다. 적을 것:
내 적응 노트 — <서비스 이름>
## 한 줄 요약
이 서비스는 ___를 한다.
## 진입점
- 런타임: src/main.ts
- 빌드: vite.config.ts
- 주요 라우트: src/routes/
## 핵심 데이터 모델
- User ─< Order ─< OrderItem
- Subscription (User에 1:1)
## 따라가 본 슬라이스
- 로그인: routes/auth → services/auth → db/users
- (다음: 주문 생성)
## 아직 모르는 것 / 질문
- [ ] 캐시 무효화는 어디서 일어나나?
- [ ] `LEGACY_MODE` 플래그는 무슨 용도?
- [ ] 왜 결제는 별도 서비스로 분리됐나?
## 헷갈렸던 것 (나중의 나에게)
- `utils/`와 `lib/`의 차이: 합의된 규칙 없음, 그냥 역사적
8.2 다이어그램 하나
글보다 그림이 빠를 때가 있습니다. 거창한 UML이 아니라, 박스와 화살표 수준이면 충분합니다.
멘탈 맵 (낯선 코드베이스, 1주차)
┌──────────┐ ┌──────────┐ ┌──────────┐
│ routes/ │────▶│ services/│────▶│ db/ │
│ (진입점) │ │ (로직) │ │ (영속화) │
└──────────┘ └────┬─────┘ └──────────┘
│
▼
┌──────────┐ ┌──────────┐
│ jobs/ │ │ external │
│ (비동기) │────▶│ APIs │
└──────────┘ └──────────┘
? = 아직 안 들어가 본 곳: jobs/ 내부, external 재시도 로직
이 그림을 그리는 행위 자체가 학습입니다. 그리다 보면 "어, 여기 화살표를 못 그리겠네 = 여기를 아직 모르네"가 드러납니다. 모르는 곳에 물음표를 찍어두면, 그게 다음에 읽을 곳의 목록이 됩니다.
8.3 질문 목록을 살아 있게
"아직 모르는 것" 목록은 적응의 핵심 도구입니다. 떠오를 때마다 적고, 답을 찾으면 지웁니다. 이 목록이 줄어드는 속도가 곧 적응의 속도입니다. 그리고 이 목록은 9장 — 사람에게 물을 때 — 의 재료가 됩니다.
머릿속 지도는 안개처럼 흩어집니다. 적어둔 지도는 남고, 다음에 들어올 사람에게 물려줄 수도 있습니다.
9장 · 무엇을, 누구에게 물을 것인가
적응은 혼자 하는 게 아닙니다. 어느 시점엔 사람에게 물어야 합니다. 하지만 무엇을, 누구에게, 어떻게 묻느냐가 결과를 가릅니다.
9.1 묻기 전에: 채팅 히스토리를 먼저 읽는다
질문하기 전에 5분만 투자하세요. 팀 채팅(Slack 등), 이슈 트래커, PR 코멘트를 검색합니다. 당신의 질문은 새로운 게 아닐 확률이 높습니다. 누군가 이미 물었고, 누군가 이미 답했습니다.
- 에러 메시지 → 채팅에서 그대로 검색
- "왜 X인가" → 관련 PR/이슈에서 검색
- 아키텍처 질문 →
docs/, 위키, 디자인 문서 검색
이걸 먼저 하면, 사람에게 갈 때 "검색해봤는데 안 나와서"라고 말할 수 있습니다. 그 한마디가 신뢰를 만듭니다.
9.2 좋은 질문 vs 나쁜 질문
| 나쁜 질문 | 좋은 질문 |
|---|---|
| "이거 어떻게 동작해요?" | "로그인 슬라이스를 따라가 봤는데, 세션이 services/session.ts에서 생성되는 것까진 봤어요. 근데 만료는 어디서 처리되나요? rg로 못 찾았어요." |
| "환경 세팅이 안 돼요" | "make setup이 3단계에서 DB_URL 누락으로 실패해요. README엔 언급이 없는데, 로컬에선 보통 어떤 값을 쓰나요?" |
| "이 부분이 이해가 안 가요" | "LEGACY_MODE 플래그가 왜 있는지 궁금해요. git blame으로 2년 전 커밋까진 찾았는데 맥락이 안 보여서요." |
좋은 질문의 공통점: 내가 어디까지 해봤는지를 보여준다. 이건 상대의 시간을 아끼는 동시에, 답을 더 정확하게 만듭니다. 상대가 "아, 거기까지 봤으면 이것만 알면 돼"라고 정밀하게 답할 수 있기 때문입니다.
9.3 누구에게 물을 것인가
- 셋업/환경 문제 → 최근에 합류한 사람. 같은 고생을 방금 했으므로 기억이 생생합니다.
- "왜 이렇게 됐나" →
git blame이 가리키는 사람, 또는 그 영역의 오래된 기여자. - 아키텍처/큰 그림 → 팀 리드 또는 시니어. 단, 정리된 질문을 모아서 한 번에.
- 막힌 작업 → 멘토나 버디가 지정돼 있으면 그 사람. 없으면 코드 리뷰어.
같은 사람에게 같은 종류의 질문을 반복하지 마세요. 질문을 모아서, 묶어서, 정리된 형태로 가져가는 것이 상대의 시간을 존중하는 방법입니다.
9.4 받은 답을 흘려보내지 않기
누군가 답을 주면, 그걸 8장의 노트에 적습니다. 같은 걸 두 번 묻지 않기 위해서이고, 다음에 들어올 사람에게 물려주기 위해서입니다. 더 좋게는, 그 답을 README나 위키에 PR로 넣으세요 — 7장의 "좋은 첫 변경"이 또 하나 생긴 셈입니다.
질문은 약점이 아닙니다. 준비 없는 질문이 약점입니다. 5분 검색하고, 어디까지 해봤는지 보여주고, 받은 답을 기록하면 — 질문은 가장 빠른 학습 도구입니다.
10장 · AI 에이전트와 함께 적응하기
2026년의 적응은 한 가지 도구가 더 생겼습니다. AI 코딩 에이전트입니다. 잘 쓰면 강력하고, 잘못 믿으면 위험합니다.
10.1 에이전트가 잘하는 것
낯선 코드베이스에서 AI 에이전트는 빠른 가이드 투어를 줍니다.
| 요청 | 에이전트가 잘하는 이유 |
|---|---|
| "이 모듈이 무슨 일을 하는지 설명해줘" | 수백 줄을 빠르게 읽고 요약 |
| "이 함수가 어디서 호출되는지 추적해줘" | grep + 정의 추적을 대신 |
| "로그인 요청의 흐름을 끝까지 따라가줘" | 4장의 수직 슬라이스를 빠르게 |
| "이 디렉터리 구조의 의도가 뭐야?" | 패턴 인식 |
| "이 낯선 라이브러리 사용법을 이 레포 안 예제로 보여줘" | 컨텍스트 안에서 예제 추출 |
특히 "설명해줘"와 "추적해줘"는 에이전트의 강점입니다. 사람이 한 시간 할 탐색을 몇 분으로 줄여줍니다.
10.2 그러나 — 투어를 맹신하지 않는다
문제는, 에이전트의 설명이 그럴듯하지만 틀릴 수 있다는 것입니다. 에이전트는 본 적 없는 부분을 가장 그럴듯한 디폴트로 채웁니다 — 즉 환각합니다. 그리고 환각은 진짜 설명과 똑같이 자신 있게 들립니다.
그래서 원칙은 하나입니다: 에이전트의 투어는 가설로 받고, 코드로 검증한다.
에이전트와 함께 적응하는 루프
1. 에이전트에게 묻는다
"결제 흐름을 설명해줘"
│
▼
2. 답을 가설로 받는다 (사실로 받지 않는다)
│
▼
3. 코드로 검증한다
- 에이전트가 말한 파일을 직접 연다
- 에이전트가 말한 함수를 rg로 찾는다
- 의심되면 디버거/로그로 확인
│
▼
4. 맞으면 노트에 적는다 / 틀리면 다시 묻는다
10.3 검증 가능한 질문을 한다
에이전트에게 물을 때, 검증하기 쉬운 형태로 묻는 것이 좋습니다.
- 나쁨: "이 시스템 안전해?" (검증 불가능, 환각 위험 큼)
- 좋음: "
createOrder를 호출하는 모든 곳을 파일 경로와 함께 나열해줘" (rg로 바로 대조 가능) - 좋음: "이 파일에서 외부 네트워크 호출을 하는 줄을 짚어줘" (직접 그 줄을 열어 확인)
구체적이고, 위치를 포함하고, 대조 가능한 질문 — 이것이 에이전트를 안전하게 쓰는 법입니다. 9장에서 사람에게 좋은 질문을 하는 원칙과 똑같습니다.
10.4 에이전트는 사람을 대체하지 않는다
에이전트는 코드에 대한 질문에 강합니다. 하지만 "왜 이런 결정을 했는가" 같은, 코드에 적히지 않은 맥락은 모릅니다. 사라진 회의, 조직의 제약, 미래의 계획 — 그건 여전히 사람에게 물어야 합니다. 에이전트는 9장을 대체하는 게 아니라, 9장에 갈 질문을 더 날카롭게 다듬어 줍니다. 코드로 답할 수 있는 건 에이전트에게, 사람만 답할 수 있는 건 사람에게.
에이전트는 훌륭한 투어 가이드입니다. 하지만 투어 가이드의 말을 받아 적기만 하는 관광객은 길을 외우지 못합니다. 묻고, 검증하고, 직접 걸어보세요.
에필로그 — 적응은 반복되는 기술이다
낯선 코드베이스 앞에 서는 일은 커리어에서 한 번 일어나는 사건이 아닙니다. 이직할 때, 팀을 옮길 때, 서비스를 인수받을 때, 오픈소스에 기여할 때 — 계속 반복됩니다.
그래서 적응은 한 번 익혀두면 평생 쓰는 기술입니다. 그리고 그 기술의 핵심은 단순합니다. 처음부터 다 읽으려 하지 말 것. 일단 돌리고, 진입점을 찾고, 형태를 보고, 하나의 길을 끝까지 따라가고, 작은 변경으로 루프를 증명하고, 지도를 적어두고, 좋은 질문을 하고, AI는 가이드로 쓰되 검증할 것. 빠르게 적응하는 사람은 더 똑똑한 게 아니라, 이 순서를 아는 사람입니다.
빠른 적응 체크리스트
- 돌렸다 — 로컬에서 앱이 켜지고 테스트가 통과한다
- 진입점을 안다 — 런타임·빌드 진입점이 어디인지 안다
- 형태를 봤다 — 디렉터리 구조, 의존성, 데이터 모델을 훑었다
- 슬라이스를 따라갔다 — 진짜 요청 하나를 끝에서 끝까지 추적했다
- 테스트를 읽었다 — 핵심 동작을 테스트로 확인했다
- 도구를 쓴다 —
rg, 정의로 이동, 호출 계층,git blame이 손에 익었다 - 작은 변경을 했다 — 첫 PR이 머지됐다(또는 리뷰 중이다)
- 지도를 적었다 — 자기만의 노트와 다이어그램, 질문 목록이 있다
- 잘 물었다 — 검색 먼저, 어디까지 해봤는지 보여주고, 답을 기록했다
- AI를 검증하며 쓴다 — 에이전트의 투어를 가설로 받고 코드로 확인한다
피해야 할 안티패턴
- 읽기만 하기 — 돌려보지 않고 코드만 읽으면 학습이 추측에 머문다
- 처음부터 다 읽기 — 형태 없이 파일을 무작위로 읽으면 둥둥 떠다닌다
- 수평으로 읽기 — 계층을 따로따로 읽지 말고, 하나의 슬라이스를 수직으로
- 혼자 오래 막히기 — 시간 상자를 정하고, 넘으면 정리된 질문으로 묻는다
- 준비 없이 묻기 — 검색도 안 하고, 어디까지 해봤는지도 없이 묻지 않는다
- 변경을 미루기 — "충분히 이해하면 그때" 하지 말고, 작은 변경을 일찍
- AI 투어 맹신 — 에이전트의 그럴듯한 설명을 사실로 받지 않는다
- 지도를 머릿속에만 — 적지 않으면 흩어지고, 다음 사람에게 못 물려준다
다음 글 예고
다음 글은 **"레거시 코드와 함께 살기 — 무서운 코드를 안전하게 바꾸는 법"**입니다. 이번 글이 "낯선 코드를 어떻게 이해하는가"였다면, 다음 글은 "이해한 그 코드가 무섭고 테스트도 없을 때, 어떻게 손대는가"입니다. 특성화 테스트, 이음새(seam) 찾기, 작은 단위의 안전한 리팩터, 그리고 레거시를 대하는 마음가짐을 다룹니다.
모든 전문가도 한때는 그 코드베이스의 새로 들어온 사람이었습니다. 차이는 적응에 걸린 시간이고, 그 시간은 방법으로 줄어듭니다. 이 글의 순서를 한 번 몸에 익히면, 다음 낯선 레포는 덜 낯설어집니다.
현재 단락 (1/272)
"적응(ramp-up)"이라는 단어를 들으면 신입 개발자의 첫 출근날을 떠올리기 쉽습니다. 하지만 현실은 다릅니다. 우리는 커리어 내내 **반복해서** 새로 들어온 사람이 됩니다.