Day 48 — 회고와 커리어 로드맵 (종합 2)
목차 18
드디어 마지막 시간이에요. 지난 시간엔 47일 동안 익혀 온 도구를 한자리에 모아, 흩어져 있던 인스타그램 코드 네 곳을 더 유연한 구조로 다듬고 테스트로 단단히 감쌌어요. 그리고 끝에 약속을 하나 남겼죠. "마지막엔 이 여정을 돌아보며 앞으로의 학습 길까지 함께 그려 보자" 고요.
오늘이 바로 그날이에요. 새 문법은 물론이고, 새 코드도 거의 안 나와요. 대신 네 가지를 천천히 함께 해요. ① 지난 시간 우리가 쓴 테스트가 정말 좋은 테스트였는지 F.I.R.S.T 다섯 성질로 점검하고, ② 47일 전체를 한 장의 지도로 정리하고, ③ 우리가 순수 자바로 만든 도메인이 다음 과목에서 어떻게 커지는지 살짝 맛보고, ④ 앞으로의 학습 길과 커리어까지 함께 그려 봐요.
그러니 오늘은 마음을 조금 편하게 가져도 돼요. 지금까지 걸어온 길을 돌아보고, 다음 한 걸음을 어디로 디딜지 정하는 시간이니까요.
🎯 학습 목표
- 좋은 테스트의 다섯 성질(F.I.R.S.T)로 우리가 쓴 테스트를 스스로 점검할 수 있어요.
- Phase 1~6, 47일 전체의 흐름을 한 장의 지도로 머릿속에 정리할 수 있어요.
- 순수 자바로 만든 도메인이 다음 과목에서 어떤 원리로 커지는지 큰 그림을 그릴 수 있어요.
- 다음 학습 로드맵과 면접·커리어의 방향을 스스로 정할 수 있어요.
Step 1: 우리 테스트를 F.I.R.S.T 로 점검해 봐요
지난 시간 우리는 리팩토링한 코드가 정말 잘 도는지 테스트로 못박았어요. 그런데 한 걸음 물러나서 물어볼 게 있어요. "그 테스트들은 좋은 테스트였을까?" 좋은 테스트의 다섯 성질을 Day 46 에서 다섯 글자로 배웠죠. F.I.R.S.T 예요.
F Fast 빠르다 — DB·네트워크 없이 메모리에서 순식간에
I Independent 독립적이다 — 테스트끼리 간섭하지 않는다
R Repeatable 반복 가능하다 — 언제 어디서 돌려도 같은 결과
S Self-validating 스스로 검증한다 — 사람 눈 없이 통과/실패가 자동
T Timely 제때 작성한다 — 코드와 같은 시점에 함께
이 다섯 글자는 외우는 용도가 아니라, "내 테스트가 혹시 느리진 않나? 다른 테스트에 기대고 있진 않나?" 를 스스로 묻는 체크리스트예요. 그럼 지난 시간 우리가 쓴 테스트를 이 다섯 잣대로 직접 채점해 볼게요.
우리 테스트 채점표
지난 시간 우리는 알림(NaiveVsStrategyTest), 피드(StreamFeedServiceTest), 회원 조회(MemberLookupTest), 통합(LikeFanoutFacadeTest) 을 테스트했어요. 다섯 성질로 하나씩 짚어 봐요.
| 성질 | 우리 테스트에서 지킨 모습 | 점검 |
|---|---|---|
| F 빠르다 | 진짜 DB 없이 인메모리 저장소만 써서 순식간에 끝나요 | ✅ |
| I 독립적이다 | @BeforeEach 로 매 테스트마다 새 세상을 다시 차렸어요 |
✅ |
| R 반복 가능하다 | 무작위 값·오늘 날짜에 안 기대요. 입력이 고정돼 있어요 | ✅ |
| S 스스로 검증한다 | assertEquals·assertThrows·assertAll 로 자동 판정 |
✅ |
| T 제때 작성한다 | 리팩토링과 같은 시점에 행동 보존 테스트를 함께 썼어요 | ✅ |
다섯 성질을 우리도 모르는 새 거의 다 지키고 있었던 거예요. 몇 개만 실제 코드로 다시 확인해 볼게요. 먼저 S(스스로 검증한다)예요.
// (지난 시간 테스트) 알림 행동 보존
String before = naive.send("email", "새 좋아요!");
String after = new NotificationSender(new EmailChannel()).notify("새 좋아요!");
assertEquals(before, after);
사람이 출력을 눈으로 읽고 "음, 같네" 하고 판단하는 게 아니라, assertEquals 가 둘이 같은지 자동으로 갈라 줘요. 이게 S 예요. 통과면 초록, 다르면 빨강 — 사람의 눈을 거치지 않아요.
다음은 R(반복 가능하다)이에요. 회원 조회 테스트는 없는 번호표를 여러 개 넣어도 항상 같은 결과가 나오는지 확인했죠.
// (지난 시간 테스트) 없는 id 는 모두 "(없는 회원)"
@ParameterizedTest
@ValueSource(longs = {0L, 2L, 99L, 1000L})
void usernameOrGuest_없으면_게스트(long missingId) {
assertEquals("(없는 회원)", lookup.usernameOrGuest(missingId));
}
입력이 0, 2, 99, 1000 으로 딱 정해져 있어요. 무작위로 뽑은 숫자가 아니라서, 오늘 돌려도 한 달 뒤에 돌려도 똑같은 결과가 나와요. 이게 R 이에요.
🙋 학생 질문 — "테스트가 어떤 날은 통과하고 어떤 날은 실패해요. 왜 그러죠?"
그렇게 들쭉날쭉한 테스트를 흔히 "흔들리는 테스트(flaky test)" 라고 불러요. 거의 항상 R(반복 가능하다)이 깨진 경우예요. 돌릴 때마다 달라지는 것 — 무작위 값, 오늘 시간, 또는 다른 테스트가 남긴 상태 — 에 기대고 있을 때 생겨요.
특히 여러 일을 동시에 처리하는 동시성 코드를 테스트할 땐 R 을 지키기가 꽤 까다로워요. 스레드 순서가 매번 달라질 수 있거든요(Day 34~35 에서 그 까다로움을 살짝 겪었죠). 그래서 좋은 테스트는 "돌릴 때마다 달라지는 것" 에 절대 기대지 않게 짜요. 흔들리는 테스트를 만나면 가장 먼저 "여기 무작위·시간·공유 상태가 끼어 있나?" 를 의심하면 돼요.
💡 오늘 기억할 한 줄: 좋은 테스트는 빠르고(F)·독립적이고(I)·반복 가능하고(R)·스스로 검증하고(S)·제때 쓰는(T) 다섯 성질을 지켜요. 우리가 쓴 테스트도 이미 이 성질을 갖추고 있었어요.
Step 2: 47일 복습 맵 (1) — 기초에서 객체 지향까지
이제 우리가 걸어온 길을 한 장의 지도로 펼쳐 볼게요. 변수 하나 선언하는 것도 낯설던 첫날부터 여기까지, 배운 걸 큰 덩어리로 묶으면 여섯 단계예요. 오늘은 둘로 나눠서, 먼저 앞쪽 절반(Phase 1~3, Day 1~24)을 봐요.
Phase 1 · 문법의 기초 (Day 1~7)
변수 → 조건문 → 반복문 → 배열 → 메서드
만든 것: 콘솔로 도는 첫 미니 프로그램
│
Phase 2 · 객체 지향 OOP (Day 8~16)
클래스 → 상속 → 다형성 → 추상 클래스 → 인터페이스 → Enum
만든 것: 인스타그램 도메인 (Member · Post · Comment · Follow)
│
Phase 3 · 자바 핵심 API (Day 17~24)
String → 컬렉션(List·Set·Map) → 제네릭 → 예외 처리
만든 것: 저장소 + 커스텀 예외를 갖춘 서비스 계층
한 단계씩 어떤 이야기였는지 떠올려 봐요. Phase 1 은 "컴퓨터에게 일을 시키는 법" 이었어요. 데이터를 변수에 담고, 조건문으로 갈래를 나누고, 반복문으로 같은 일을 되풀이하고, 메서드로 기능을 쪼갰죠. 여기까지가 프로그래밍의 기본 골격이에요.
Phase 2 에서 큰 전환이 있었어요. 흩어진 데이터와 기능을 "객체" 라는 하나의 덩어리로 묶기 시작했죠. 설계도(클래스)로 실체(객체)를 찍어내고, 상속으로 코드를 물려받고, 다형성으로 같은 명령에 다르게 반응하게 만들고, 인터페이스로 "역할" 을 약속했어요. 그 결과 인스타그램의 회원·게시물·댓글·팔로우를 순수 자바 객체로 설계했어요.
Phase 3 은 실전에서 매일 쓰는 도구함이었어요. 문자열을 제대로 다루고, List·Set·Map 으로 데이터를 효율적으로 관리하고, 제네릭으로 타입을 안전하게 묶고, 예외로 에러를 체계적으로 처리했죠. 그래서 진짜 서비스처럼 도는 저장소와 서비스 계층을 만들 수 있었어요.
여기서 한 가지 눈여겨볼 게 있어요. 세 단계가 따로 논 게 아니라, 인스타그램이라는 하나의 프로젝트를 같이 키워 왔다는 점이에요.
Phase 1 → 콘솔로 사용자 데이터를 다루기
Phase 2 → 그 데이터를 Member · Post 객체로 승격
Phase 3 → 객체를 저장하고 찾고, 에러를 처리하는 서비스로 확장
🙋 학생 질문 — "복습은 어떻게 하면 좋을까요? 47일치를 다 다시 보긴 너무 많아요."
다 다시 볼 필요는 없어요. 이 지도를 길잡이로 쓰면 돼요. 각 Phase 제목을 보고 "이건 잘 안다" 와 "이건 가물가물하다" 를 솔직하게 표시해 보세요. 가물가물한 곳만 그 Day 교안과 코드를 다시 열어 보면 충분해요.
그리고 가장 좋은 복습은 직접 손으로 다시 짜 보는 거예요. 머리로 "안다" 와 손으로 "짤 수 있다" 는 생각보다 멀거든요. 작은 기능 하나를 빈 화면에서 처음부터 다시 만들어 보면, 어디가 약한지가 바로 드러나요.
💡 오늘 기억할 한 줄: 앞쪽 절반은 문법의 기초(Phase 1) → 객체 지향(Phase 2) → 핵심 API(Phase 3) 로 쌓였고, 그동안 인스타그램 도메인이 콘솔에서 서비스 계층까지 함께 자랐어요.
Step 3: 47일 복습 맵 (2) — 모던 자바에서 설계의 깊이까지
이제 뒤쪽 절반이에요(Phase 4~6, Day 25~47). 앞쪽이 "기본기를 쌓는 길" 이었다면, 뒤쪽은 "코드를 더 짧고·더 크고·더 깊게 다루는 길" 이었어요.
Phase 4 · 모던 자바 (Day 25~32)
람다 → Stream → Optional → Record → (Gradle · 전체 복습)
효과: 코드가 짧아지고, "무엇을 한다"가 그대로 읽혀요
│
Phase 5 · 동시성과 입출력 (Day 33~39) · 심화
Thread → 동시성의 함정 → ExecutorService → CompletableFuture
→ 가상 스레드 → 파일 I/O (java.io · java.nio)
│
Phase 6 · 설계와 깊이 (Day 40~48) · 심화
디자인 패턴 → JVM 메모리·GC → 클래스 로딩·리플렉션 → JUnit
→ 최종 종합 (지난 시간 리팩토링 · 오늘 회고)
Phase 4 는 코드를 다듬는 시간이었어요. 람다와 Stream 으로 "어떻게 도느냐" 를 일일이 쓰는 대신 "무엇을 한다" 를 선언하게 됐고, Optional 로 null 을 안전하게 감쌌고, Record 로 데이터 클래스를 한 줄에 담았죠. 같은 일을 훨씬 짧고 또렷하게 쓰는 법을 배운 거예요.
Phase 5 는 여기서부터 심화예요. 여러 일을 동시에 처리하는 법(스레드·동시성), 그 동시성이 만드는 함정(경쟁 상태·데드락), 비동기를 조합하는 법(CompletableFuture), 그리고 파일을 읽고 쓰는 법(I/O)을 다뤘어요. 한 번에 더 큰 일을 다루는 체력을 키운 단계죠.
Phase 6 은 시니어로 가는 길이었어요. 좋은 구조를 만드는 디자인 패턴, JVM 이 메모리를 다루는 원리, 어노테이션을 읽는 리플렉션, 그리고 코드를 지키는 JUnit. 마지막으로 지난 시간엔 이 모든 도구를 한 도메인에서 동시에 꺼내 썼고, 오늘 이렇게 돌아보고 있어요.
지난 시간을 떠올려 보세요. 알림은 패턴으로(Phase 6), 피드는 Stream 으로(Phase 4), 회원 조회는 Optional 로(Phase 4), 그리고 전부 JUnit 으로 감쌌어요(Phase 6). 47일치 도구가 한 흐름에서 만난 거예요. 그게 가능했던 건, 여섯 단계가 차곡차곡 위로 쌓여 왔기 때문이에요.
💡 오늘 기억할 한 줄: 뒤쪽 절반은 모던 자바(Phase 4)로 짧게, 동시성·I/O(Phase 5)로 크게, 설계와 깊이(Phase 6)로 깊게 나아갔어요. 지난 시간 종합이 이 모든 도구가 한자리에 모인 장면이었죠.
Step 4: 순수 자바 "두뇌"에 "몸" 입히기 — 다음 과목 미리보기
47일 동안 우리가 만든 인스타그램은 사실 "두뇌" 예요. 회원이 어떤 데이터를 갖는지, 좋아요를 누르면 무슨 일이 일어나는지, 팔로우 관계가 어떻게 얽히는지 — 서비스의 핵심 논리를 순수 자바로 또렷하게 설계했죠. 그런데 아직 "몸" 이 없어요. 바깥세상(사용자의 휴대폰, 진짜 데이터베이스)과 연결되는 통로가 없거든요.
다음 과목에서 바로 그 "몸" 을 입혀요. 그게 백엔드 프레임워크예요. 우리가 손으로 짜던 번거로운 일들을 프레임워크가 대신 해 줘요.
순수 자바로 만든 도메인 (우리의 47일)
│
▼ 다음 과목에서 프레임워크가 '몸'을 입혀요
├─ 클래스가 DB 테이블과 자동으로 이어져요
├─ 객체를 직접 만들지 않아도 자동으로 연결돼요
└─ 웹 요청이 알맞은 메서드로 알아서 들어와요
놀라운 건, 다음 과목에서 새로 배우는 건 "프레임워크가 대신 해 주는 방식" 이지 자바 자체가 아니라는 점이에요. 우리가 47일간 배운 것 위에 얇은 층이 한 겹 얹히는 거예요. 우리가 만든 것이 다음 과목에서 어떻게 커지는지 원리로만 짚어 볼게요.
| 우리가 47일간 만든 것 | 다음 과목에서 커지는 모습 (원리) |
|---|---|
| 도메인 클래스 (Member·Post) | DB 테이블과 자동으로 이어지는 객체 |
| 컬렉션 기반 저장소 | 진짜 데이터베이스를 다루는 저장소 |
| 커스텀 예외 계층 (Day 21~23) | 모든 에러를 한곳에서 받아 응답으로 |
| 생성자로 객체 넘겨받기 (Day 24) | 프레임워크가 필요한 객체를 자동으로 이어붙여 줌 |
제네릭 <T> (Day 20) |
저장소·응답을 감싸는 일반화된 그릇 |
| Record DTO (Day 29) | 요청과 응답을 실어 나르는 상자 |
여기서 딱 하나만 기억하면 돼요. 다음 과목의 마법처럼 보이는 자동화들은, 전부 우리가 배운 자바 위에서 돌아간다는 거예요. 예를 들어 "객체를 자동으로 이어붙여 준다" 는 건, Day 24 에서 우리가 손으로 "객체를 직접 만들지 않고 생성자로 넘겨받기" 를 했던 그 일을 프레임워크가 대신 해 주는 것뿐이에요. 그래서 자바 기본기가 탄탄하면 다음 과목이 훨씬 쉽게 들어와요.
⚠️ 위 표의 자동화 방식에는
@로 시작하는 짧은 표시(어노테이션)가 쓰여요. 그 정확한 글자들은 지금 외울 필요가 전혀 없어요 — 다음 과목 첫날에 처음 차근차근 만나게 돼요. 오늘은 "아, 우리가 만든 두뇌에 몸이 입혀지는구나" 라는 큰 그림만 가져가면 충분해요.
🙋 학생 질문 — "그럼 다음 과목부터는 순수 자바는 이제 안 쓰나요?"
전혀 아니에요. 오히려 그 반대예요. 프레임워크라는 "몸" 아래에는 우리가 배운 자바가 그대로 깔려 있어요. 클래스·상속·인터페이스·컬렉션·예외·Stream·Optional — 전부 다음 과목 코드 안에서 매 순간 쓰여요.
프레임워크는 자바를 대체하는 게 아니라 자바를 편하게 부려 쓰는 도구예요. 그래서 자바 기초가 약하면 프레임워크가 마법처럼만 보이고, 뭔가 어긋났을 때 손을 못 대요. 반대로 우리처럼 기초가 단단하면, 프레임워크가 안에서 무슨 일을 하는지 짐작이 가서 훨씬 든든하게 배울 수 있어요.
💡 오늘 기억할 한 줄: 우리가 만든 순수 자바 도메인은 "두뇌" 이고, 다음 과목의 프레임워크는 그 위에 "몸" 을 입혀요. 자동화처럼 보이는 것들도 결국 우리가 배운 자바 위에서 도니까, 기본기가 탄탄할수록 다음 과목이 쉬워져요.
Step 5: 다음 학습 로드맵 — 풀스택으로 가는 지도
"자바는 끝났는데, 이제 어디로 가야 하죠?" 가장 많이 받는 질문이에요. 큰 길을 지도로 그려 볼게요. 우리는 방금 출발점에 서 있어요.
지금 여기 ─ Java 언어 완주 (java-basic, 47일!)
│
▼
spring-boot ─ 백엔드 프레임워크로 '몸' 입히기 (웹 API · DB 연결)
│
▼
데이터베이스 ─ SQL·관계형 DB를 더 깊이 (MySQL · PostgreSQL)
│
▼
인프라 ─ 만든 서비스를 실제 사용자에게 배포하고 운영하기
│
▼
(관심 따라) 프론트엔드 · 데이터/AI 로 넓혀 가기
가장 자연스러운 다음 걸음은 🌟 spring-boot 예요. 우리가 만든 두뇌에 몸을 입혀 진짜로 도는 웹 서비스를 만드는 과목이거든요. 참고로 2026년 현재 백엔드 자바의 표준은 Spring Boot 4 + JDK 25 예요. 우리가 java-basic 을 JDK 25 로 배운 게 그래서예요 — 다음 과목과 버전이 그대로 이어져요.
그다음은 데이터베이스를 더 깊이 파고(우리가 인메모리로 흉내 낸 저장소를 진짜 DB 로), 만든 서비스를 세상에 배포하는 인프라로 넓혀 가요. 프론트엔드나 데이터·AI 는 관심에 따라 곁가지로 더하면 돼요.
여기서 솔직하게 한마디 할게요. 요즘 신입 개발자 시장이 예전보다 좁아진 건 사실이에요. 하지만 겁먹을 필요는 없어요. 회사들이 여전히 가장 많이 찾는 건 Spring Boot 를 다룰 줄 아는 자바 백엔드 개발자 거든요(국내 상위 기업 다수가 Spring 계열을 써요). 우리가 걷는 길이 바로 그 길이에요. 한 번에 다 잘하려 하지 말고, 이 지도를 한 칸씩 또박또박 밟아 가면 돼요.
💡 오늘 기억할 한 줄: 다음 한 걸음은 spring-boot(우리 두뇌에 몸 입히기) → 데이터베이스 → 인프라 순서가 자연스러워요. 2026년 표준은 Spring Boot 4 + JDK 25 라, 우리가 배운 버전이 그대로 이어져요.
Step 6: 면접 준비 — 무엇을, 어떻게 보여줄까
"면접" 이라는 말만 들어도 긴장되죠? 그런데 좋은 소식이 있어요. 신입 백엔드 면접에서 단골로 나오는 질문들을, 우리는 이미 47일 동안 다 배웠어요. 새로 외울 게 아니라, 배운 걸 "설명할 수 있게" 정리하면 되는 거예요.
| 면접에서 자주 나오는 질문 | 우리가 배운 곳 |
|---|---|
"== 와 equals 는 뭐가 다른가요?" |
Day 17 (String 불변) |
"HashMap 은 어떻게 키로 값을 빨리 찾나요?" |
Day 19 (equals/hashCode) |
| "오버라이딩과 오버로딩의 차이는요?" | Day 6 · Day 10 |
| "여러 스레드가 같은 값을 동시에 고치면요?" | Day 34 (경쟁 상태) |
| "GC 가 뭐고, 메모리는 어떻게 관리되나요?" | Day 43~44 |
| "예외는 어디서 잡는 게 좋을까요?" | Day 21~23 |
"Stream 과 for 문, 언제 무엇을 쓰나요?" |
Day 26 · Day 47 |
표를 보면 마음이 좀 놓이지 않나요? 면접관이 묻는 건 대단한 비법이 아니라, 우리가 이미 다져 온 기본기예요. 차이는 "안다" 를 "남에게 설명한다" 로 한 단계 올리는 것뿐이에요. 그래서 면접 준비의 핵심은, 배운 개념을 친구에게 말로 설명해 보는 거예요. 막히는 부분이 곧 다시 봐야 할 부분이고요.
그리고 면접에서 가장 강력한 건 말보다 직접 만든 것 이에요. 우리에겐 이미 좋은 재료가 있어요. 47일 동안 키운 인스타그램 도메인, 그리고 그걸 지키는 테스트들이요. "이 좋아요 기능을 처음엔 if-else 로 짰다가, 확장이 어려워서 패턴으로 다시 다듬었어요. 그리고 동작이 똑같다는 걸 테스트로 증명했어요" — 이 한마디가, 외운 지식 열 개보다 훨씬 깊은 인상을 줘요.
🙋 학생 질문 — "아직 실력이 부족한 것 같은데, 면접을 봐도 될까요?"
당연히 봐도 돼요. 그리고 사실, "완벽해지면 지원해야지" 하고 기다리면 그 날은 영영 안 와요. 현업에 있는 개발자들도 모르는 게 매일 새로 생기거든요.
면접관도 신입에게 모든 걸 다 알기를 기대하지 않아요. 오히려 "모르는 걸 어떻게 풀어 가는지", "기본기가 탄탄한지", "함께 일하면서 배울 사람인지" 를 봐요. 모르는 질문이 나오면 아는 척하기보다 "거기까진 아직 안 써 봤는데, 제가 아는 선에서 추측해 보면…" 하고 솔직하게 풀어 가는 쪽이 훨씬 좋은 인상을 줘요. 우리가 47일 동안 쌓은 기본기면, 출발선에 설 자격은 충분해요.
💡 오늘 기억할 한 줄: 면접 단골 질문은 대부분 우리가 이미 배운 기본기예요. "안다" 를 "설명할 수 있다" 로 올리고, 직접 만든 인스타그램 도메인과 테스트를 보여 주면 그게 가장 강한 무기예요.
Step 7: 커리어 마인드셋 — AI 시대, 그리고 첫 직장
마지막으로, 조금 더 멀리 보는 이야기예요. "AI 가 코드를 다 짜 주는 시대에, 제가 배운 게 의미가 있을까요?" 요즘 가장 많이 걱정하는 부분이죠. 정면으로 이야기해 볼게요.
AI 코딩 도구는 정말 많은 걸 대신해 줘요. 반복되는 코드, 간단한 버그 수정, 뼈대 잡기 — 예전엔 신입이 한나절 걸리던 일을 몇 분 만에 해내요. 그래서 "시키는 대로 코드만 찍어내는 일" 의 가치는 분명히 줄었어요. 솔직히 신입 채용이 예전보다 까다로워진 데에는 이런 변화도 있어요.
그런데 바로 여기에 반전이 있어요. AI 가 코드를 대신 써 줄수록, 그 코드가 왜 맞는지 읽고·검증하고·고칠 줄 아는 사람 이 더 귀해져요. AI 는 그럴듯한 코드를 빠르게 내놓지만, 그게 정말 옳은지·우리 상황에 맞는지는 판단하지 못하거든요. 그 판단을 하는 게 바로 사람의 일이에요.
AI 가 잘하는 것 사람이 해야 하는 것 (우리의 47일)
───────────────────────────────────────────────────────
빠르게 코드 초안 만들기 → 이 코드가 왜 맞는지 이해하기
뼈대·반복 코드 찍어내기 → 숨은 버그·동시성 함정 찾아내기
문법 알려주기 → "왜 이 구조인가" 설계 판단하기
테스트 코드 생성하기 → 그 테스트가 좋은 테스트인지 보기 (F.I.R.S.T)
오른쪽 칸이 눈에 익지 않나요? 우리가 47일 동안 배운 게 정확히 그거예요. "외우지 말고 이해하자" 를 줄곧 강조한 이유가 여기 있어요. AI 를 잘 쓰는 신입은 AI 의 답을 그대로 베끼는 사람이 아니라, AI 의 답을 검증하고 "왜 이게 되는지" 를 이해해서 자기 것으로 만드는 사람이에요. 그 힘의 바탕이 바로 기본기예요.
그러니 첫 직장에 너무 큰 의미를 두지 않아도 돼요. 첫 직장은 종착지가 아니라 출발선이에요. 개발자의 진짜 실력은 입사 순간이 아니라, 그 뒤로 꾸준히 모르는 걸 하나씩 메워 가며 자라요. 오늘 47일을 완주한 그 꾸준함이, 앞으로의 긴 길에서 가장 든든한 무기가 될 거예요.
💡 오늘 기억할 한 줄: AI 가 코드를 대신 쓰는 시대일수록, 그 코드를 읽고·검증하고·이해하는 기본기가 더 귀해져요. 우리가 47일간 "이해" 에 집중한 게 바로 그 힘이에요. 첫 직장은 종착지가 아니라 출발선이에요.
마무리
오늘은 코드 한 줄 없이, 47일을 돌아보고 다음 길을 그린 하루였어요. 네 가지를 함께 했죠.
- F.I.R.S.T 점검 — 우리가 쓴 테스트가 빠르고·독립적이고·반복 가능하고·스스로 검증하고·제때 쓰인, 좋은 테스트였음을 다섯 잣대로 확인했어요.
- 47일 복습 맵 — 문법의 기초 → 객체 지향 → 핵심 API → 모던 자바 → 동시성·I/O → 설계와 깊이, 여섯 단계가 어떻게 쌓였는지 한 장으로 정리했어요.
- 다음 과목 미리보기 — 우리가 만든 순수 자바 "두뇌" 에 다음 과목이 "몸" 을 입히는 큰 그림을 봤어요.
- 로드맵과 커리어 — spring-boot 로 가는 길, 면접 준비, 그리고 AI 시대에 기본기가 왜 더 귀한지 함께 짚었어요.
처음 변수 하나를 선언하던 날을 떠올려 보세요. int 가 뭔지, 세미콜론은 왜 찍는지조차 낯설었죠. 그런데 지금 여러분은 객체로 도메인을 설계하고, 패턴으로 구조를 다듬고, 테스트로 코드를 지키고, 그게 왜 그렇게 도는지 설명할 수 있는 사람이 됐어요. 47일 동안 정말 먼 길을 걸어오셨어요.
이제 java-basic 의 여정은 여기서 마무리돼요. 하지만 끝이 아니라, 더 큰 세계로 가는 문 앞에 선 거예요. 우리가 순수 자바로 또렷하게 만든 인스타그램의 "두뇌" 는, 다음 과목 spring-boot 에서 진짜로 도는 웹 서비스의 "몸" 을 입게 돼요. 거기서 우리가 배운 클래스·인터페이스·컬렉션·예외·Stream 이 매 순간 다시 등장할 거예요. 그때 "아, 이거 java-basic 에서 봤던 거다!" 하는 순간이 자주 올 거예요.
긴 여정 함께 걸어 주셔서 고마웠어요. 다음 과목에서, 한 단계 더 성장한 모습으로 다시 만나요. 정말 수고 많으셨어요! 🌟
과제
오늘은 코드 과제 대신, 47일을 내 것으로 정리하는 회고 과제예요. 천천히, 나를 위해 채워 보세요.
과제 1: 나만의 47일 복습 맵 그리기 [기초]
Step 2~3 에서 본 여섯 단계(Phase 1~6) 지도를 직접 손으로 다시 그려 보세요. 각 Phase 옆에 두 가지를 표시하는 게 핵심이에요 — "이건 자신 있다(🌟)" 와 "이건 다시 봐야겠다(다시)". 그리고 "다시" 라고 표시한 것 중 딱 하나를 골라, 그 Day 의 개념을 빈 화면에서 작은 예제로 다시 짜 보세요. 머리로 아는 것과 손으로 짜는 것의 차이를 직접 느껴 보는 게 목적이에요.
과제 2: 내 테스트를 F.I.R.S.T 로 채점하기 [응용]
지난 시간(또는 그 전에) 내가 쓴 테스트 하나를 골라, Step 1 의 채점표처럼 F.I.R.S.T 다섯 성질을 하나씩 따져 보세요. 각 글자마다 "지켰다 / 애매하다 / 못 지켰다" 를 적고, 왜 그렇게 판단했는지 한 줄씩 근거를 다세요. 특히 R(반복 가능하다)을 깨뜨릴 만한 요소(무작위 값·시간·공유 상태)가 숨어 있지 않은지 꼼꼼히 살펴보세요. 만약 애매한 항목이 있다면, 어떻게 고치면 그 성질을 지킬 수 있을지도 한 문장으로 적어 보세요.
과제 3: "두뇌 → 몸" 한 줄 매핑 + 면접 답변 준비 [심화]
다음 과목 진입을 미리 준비하는 과제예요. 두 가지 중 하나(또는 둘 다)를 골라 해 보세요. (가) 우리가 만든 도메인 클래스 하나(예: Member)를 골라, 그것이 다음 과목에서 어떻게 커질지 Step 4 의 표를 참고해 원리로만 두세 줄 적어 보세요(어노테이션 글자는 몰라도 괜찮아요. "DB 테이블과 이어진다" 같은 원리면 충분해요). (나) Step 6 의 면접 단골 질문 중 세 개를 골라, 각각을 친구에게 설명하듯 입으로 소리 내어 답해 보고, 막힌 부분이 어디였는지 적어 보세요.
생각해볼 주제
1. 테스트는 많을수록 좋은 걸까?
"코드의 모든 줄이 테스트로 덮여 있어야 좋은 코드다" 라는 말을 들어 봤을 거예요. 그런데 정말 테스트는 무조건 많을수록 좋을까요? 테스트도 결국 우리가 관리해야 할 코드예요. 프로덕션 코드가 바뀌면 거기 묶인 테스트도 함께 고쳐야 하죠. F.I.R.S.T 다섯 성질을 떠올리면서, "좋은 테스트 100개" 와 "그냥 테스트 1000개" 중 무엇이 더 가치 있을지, 테스트의 양과 질 사이에서 무엇을 기준으로 삼아야 할지 생각해 보세요.
2. AI 가 코드를 다 써 주는 시대에, 우리는 왜 기본기를 배울까?
AI 코딩 도구는 이제 웬만한 코드를 빠르게 만들어 줘요. 그렇다면 "어차피 AI 가 짜 줄 텐데 굳이 자바 문법을 깊이 알아야 하나?" 라는 의문이 들 수 있어요. 하지만 AI 가 내놓은 코드가 항상 옳은 건 아니에요. 그 코드가 맞는지 누가, 어떻게 판단할까요? AI 시대에 개발자의 역할은 "코드를 쓰는 사람" 에서 무엇으로 바뀌고 있는지, 그리고 그 변화 속에서 기본기가 왜 약점이 아니라 무기가 되는지 생각해 보세요.
3. 나는 어떤 개발자로 성장하고 싶은가 — 넓게 vs 깊게
앞으로 배울 게 정말 많아요. spring-boot, 데이터베이스, 인프라, 프론트엔드, AI… 한 사람이 이 모든 걸 다 깊게 알 수는 없어요. 그래서 자주 나오는 고민이 "여러 분야를 넓게 알아야 할까, 한 분야를 깊게 파야 할까" 예요. 흔히 말하는 "T 자형 인재"(한 분야는 깊게, 주변은 넓게)는 어떤 의미일까요? 지금 막 출발선에 선 신입에게는 넓이와 깊이 중 무엇을 먼저 챙기는 게 좋을지, 나는 어떤 방향으로 성장하고 싶은지 그려 보세요.
✅ 예시 답안정답 보기
오늘 과제는 정답이 하나로 정해진 코드 문제가 아니에요. 그래서 아래 답안은 "이렇게 정리하면 좋아요" 라는 본보기예요. 내 답이 이것과 똑같지 않아도 전혀 괜찮아요. 중요한 건 직접 돌아보고 내 언어로 정리해 보는 거예요.
🎯 [과제 1 예시 답안] 나만의 47일 복습 맵 그리기
먼저 여섯 단계 지도를 직접 그리고, 각 Phase 옆에 "자신 있다(🌟)" 와 "다시 봐야겠다(다시)" 를 솔직하게 표시해요. 아래는 한 학생이 채운 예시예요.
Phase 1 · 문법의 기초 변수·조건·반복·배열·메서드 🌟 자신 있다
Phase 2 · 객체 지향 클래스·상속·다형성·인터페이스 다시 (다형성이 가물가물)
Phase 3 · 핵심 API String·컬렉션·제네릭·예외 🌟 (예외는 자신 있음)
Phase 4 · 모던 자바 람다·Stream·Optional·Record 다시 (flatMap 헷갈림)
Phase 5 · 동시성·I/O Thread·동시성·파일 다시 (제일 어려웠음)
Phase 6 · 설계와 깊이 패턴·JVM·리플렉션·JUnit 🌟 (패턴은 재밌었음)
표시가 끝나면 "다시" 중 하나를 골라 작은 예제로 다시 짜 봐요. 예를 들어 "Phase 4 의 flatMap" 을 골랐다면, 거창한 코드 말고 이렇게 작은 걸로 충분해요.
// "묶음 안의 묶음"을 한 줄기로 펴는 flatMap 을 손으로 다시 확인
List<List<String>> grouped = List.of(
List.of("민지글1", "민지글2"),
List.of("승우글1")
);
List<String> flat = grouped.stream()
.flatMap(List::stream) // 묶음들을 한 흐름으로 펼쳐요
.toList();
System.out.println(flat); // [민지글1, 민지글2, 승우글1]
💡 이 과제의 핵심
복습의 목적은 "다 외우는 것" 이 아니라 "내가 어디가 약한지 아는 것" 이에요. 약한 곳을 알면 그곳만 집중해서 메우면 되니까 훨씬 효율적이죠. 그리고 머리로 "안다" 와 손으로 "짤 수 있다" 는 다르다는 걸, 위처럼 빈 화면에서 작은 예제를 다시 짜 보면 바로 느낄 수 있어요. 막히는 순간이 곧 다시 봐야 할 지점이에요.
🎯 [과제 2 예시 답안] 내 테스트를 F.I.R.S.T 로 채점하기
지난 시간에 쓴 테스트 하나를 골라 다섯 성질로 따져 봐요. 여기서는 회원 조회 테스트를 예로 들게요.
@ParameterizedTest
@ValueSource(longs = {0L, 2L, 99L, 1000L})
void usernameOrGuest_없으면_게스트(long missingId) {
assertEquals("(없는 회원)", lookup.usernameOrGuest(missingId));
}
이 테스트를 다섯 잣대로 채점한 표예요.
| 성질 | 판정 | 근거 |
|---|---|---|
| F 빠르다 | 지켰다 | 인메모리 lookup 만 쓰고 DB·네트워크가 없어 순식간에 끝나요 |
| I 독립적이다 | 지켰다 | @BeforeEach 로 매번 새 lookup 을 만들어 다른 테스트와 안 얽혀요 |
| R 반복 가능하다 | 지켰다 | 입력이 0,2,99,1000 으로 고정 — 무작위·시간에 안 기대요 |
| S 스스로 검증한다 | 지켰다 | assertEquals 가 통과/실패를 자동으로 갈라요 |
| T 제때 작성한다 | 지켰다 | 조회 기능을 만든 그 시점에 함께 썼어요 |
애매한 테스트는 이렇게 고쳐요
만약 R(반복 가능하다)이 애매한 테스트를 만났다면, 보통 "돌릴 때마다 달라지는 것" 이 숨어 있어요. 예를 들어 이런 코드예요.
// ❌ R 이 깨진 테스트 — 오늘 날짜에 기대고 있어요
@Test
void 가입연도_확인() {
Member m = new Member("jaehoon", "j@insta.com");
int thisYear = LocalDate.now().getYear(); // 돌리는 해마다 값이 달라져요!
assertEquals(thisYear, m.getJoinYear());
}
LocalDate.now() 는 올해가 2026 년이면 2026, 내년이면 2027 을 줘요. 그래서 해가 바뀌면 갑자기 실패할 수 있죠. 이럴 땐 "달라지는 값" 을 바깥에서 고정해 넣어 주면 R 이 살아나요.
// ✅ 고정된 값을 넣어 언제 돌려도 같은 결과
@Test
void 가입연도_확인() {
Member m = new Member("jaehoon", "j@insta.com", 2025); // 연도를 고정
assertEquals(2025, m.getJoinYear());
}
💡 이 과제의 핵심
F.I.R.S.T 는 점수를 매기려는 게 아니라, "내 테스트가 혹시 약한 곳이 있나" 를 스스로 비춰 보는 거울이에요. 특히 R 은 초보자가 가장 자주 놓치는 성질이라, "무작위·시간·다른 테스트가 남긴 상태" 이 셋만 의심해도 흔들리는 테스트의 대부분을 잡아낼 수 있어요.
🎯 [과제 3 예시 답안] "두뇌 → 몸" 매핑 + 면접 답변 준비
(가) 도메인 클래스가 다음 과목에서 커지는 모습 — Member 예시
우리가 만든 Member 클래스를 골라, 다음 과목에서 어떻게 커질지 원리로만 적어 봤어요. 정확한 어노테이션 글자는 몰라도 괜찮아요.
Member클래스는 지금은 메모리 위에서만 사는 객체예요. 다음 과목에선 이 클래스가 데이터베이스의members테이블과 자동으로 이어져요. 그래서Member객체 하나를 저장하면, 우리가 직접 SQL 을 쓰지 않아도 테이블에 한 줄이 쌓여요. 또 지금은new Member(...)로 직접 만들고 저장소를 생성자로 넘겨줬는데(Day 24), 다음 과목에선 필요한 객체들을 프레임워크가 알아서 이어붙여 줘요. 우리가 손으로 하던 "객체 넘겨받기" 를 도구가 대신 해 주는 거예요.
핵심은, 새로 생기는 건 "프레임워크가 대신 해 주는 방식" 이지 Member 안의 자바 코드(필드·생성자·메서드)는 거의 그대로라는 점이에요.
(나) 면접 단골 질문에 답해 보기
Step 6 의 질문 중 세 개를 골라, 친구에게 설명하듯 답을 정리해 봤어요.
"== 와 equals 는 뭐가 다른가요?" (Day 17)
==는 두 변수가 가리키는 게 "같은 물건" 인지를 봐요(같은 주소인지).equals는 "내용이 같은지" 를 봐요. 그래서 글자는 똑같은데 서로 다른 곳에 만들어진 두 문자열을==로 비교하면false가 나올 수 있고,equals로 비교하면true가 나와요. 그래서 문자열이나 객체의 내용을 비교할 땐equals를 써요.
"HashMap 은 어떻게 키로 값을 그렇게 빨리 찾나요?" (Day 19)
키의
hashCode로 "어느 칸에 넣을지" 를 먼저 계산해서, 그 칸으로 곧장 가요. 그래서 모든 칸을 하나씩 뒤지지 않고 거의 한 번에 찾아요. 단, 이게 제대로 되려면 키 객체가equals와hashCode를 짝 맞게 구현해야 해요. 안 그러면 분명 넣었는데 못 찾는 일이 생겨요.
"여러 스레드가 같은 값을 동시에 고치면 어떻게 되나요?" (Day 34)
"경쟁 상태" 가 생길 수 있어요. 예를 들어 두 스레드가 좋아요 수를 동시에 1씩 올리면, 둘 다 같은 값을 읽고 각자 1을 더해서 결과적으로 1만 오르는 일이 벌어져요. 둘이 올렸는데 하나만 오른 거죠. 그래서 같은 값을 여럿이 동시에 건드릴 땐, 한 번에 한 명만 들어가게 막아 주는 장치(
synchronized같은)가 필요해요.
💡 이 과제의 핵심
면접 답변은 외운 문장을 토해 내는 게 아니라, "내 말로 설명할 수 있는가" 가 전부예요. 위 답들처럼 짧은 비유나 예시를 곁들여 설명해 보면, 막히는 부분이 어디인지 바로 드러나요. 그 막힌 곳이 다시 공부할 곳이고요. 소리 내어 말해 보는 연습이 눈으로 읽는 것보다 훨씬 효과가 커요.
🤔 생각해볼 주제 예시 답안
아래는 정답이 아니라 "이렇게 생각을 풀어 가면 좋아요" 라는 길잡이예요. 각 주제 끝에는 면접에서 한 방에 정리할 수 있는 핵심 멘트를 붙여 뒀어요.
주제 1. 테스트는 많을수록 좋은 걸까?
[문제 상황 요약]
"코드의 모든 줄을 테스트로 덮어야 좋은 코드" 라는 말이 있어요. 그런데 테스트도 우리가 관리해야 할 코드예요. 무조건 많이 짜는 게 정말 이득일까요?
[튜터의 가이드 및 해설]
테스트는 양보다 질이에요. 이유는 두 가지예요.
첫째, 테스트도 유지보수 비용이 있어요. 프로덕션 코드가 바뀌면 거기 묶인 테스트도 함께 고쳐야 해요. 그런데 테스트가 내부 구현에 너무 시시콜콜 매여 있으면, 동작은 그대로인데 구조만 살짝 바꿔도 테스트가 우수수 깨져요. 그러면 테스트가 오히려 리팩토링의 발목을 잡아요.
둘째, 숫자(커버리지 100%)는 착시를 줘요. 모든 줄을 한 번씩 실행했다는 것과, 중요한 동작·경계·예외를 제대로 검증했다는 건 달라요. assert 없이 그냥 호출만 하는 테스트도 커버리지는 올려 주거든요. 그건 안전망이 아니에요.
그래서 기준은 F.I.R.S.T 와 "무엇을 검증하는가" 예요. 핵심 동작, 자주 틀리는 경계(빈 값·없는 값), 터져야 할 예외 — 이런 가치 있는 곳을 좋은 테스트로 덮는 게, 의미 없는 테스트 1000개보다 훨씬 든든해요.
🎯 면접관을 홀리는 핵심 멘트
"테스트는 양이 아니라 질이라고 생각합니다. 커버리지 100% 가 목표가 되면
assert없이 호출만 하는 빈 테스트로도 숫자를 채울 수 있어서 착시가 생깁니다. 저는 핵심 동작·경계·예외처럼 가치 있는 지점을, F.I.R.S.T 를 지키는 좋은 테스트로 덮는 걸 우선합니다. 또 테스트가 내부 구현에 과하게 묶이면 리팩토링 때 우수수 깨져 오히려 발목을 잡으므로, '무엇을 하는가' 를 검증하고 '어떻게 하는가' 엔 덜 매이게 짭니다."
주제 2. AI 시대에, 우리는 왜 기본기를 배울까?
[문제 상황 요약]
AI 코딩 도구가 웬만한 코드를 빠르게 만들어 줘요. "어차피 AI 가 짜 줄 텐데 굳이 자바를 깊이 알아야 하나?" 라는 의문이 들 수 있어요.
[튜터의 가이드 및 해설]
AI 가 코드를 빠르게 내놓는 건 맞아요. 반복 코드·뼈대·간단한 버그 수정은 정말 잘해요. 그래서 "시키는 대로 코드만 찍는 일" 의 가치는 줄었어요. 여기까진 사실이에요.
그런데 AI 가 못 하는 게 있어요. AI 는 그럴듯한 코드를 내놓지만, 그게 정말 옳은지·우리 상황에 맞는지·숨은 버그는 없는지 판단하지 못해요. 그 판단을 하는 게 사람이에요. AI 가 짠 코드에 동시성 함정이 숨어 있는지, 이 자료구조가 이 상황에 맞는지, 메모리가 새지 않는지를 읽어내려면 기본기가 있어야 해요.
그래서 AI 시대에 개발자의 역할은 "코드를 쓰는 사람" 에서 "코드를 읽고·검증하고·판단하는 사람" 으로 옮겨가고 있어요. AI 를 잘 쓰는 신입은 AI 의 답을 베끼는 사람이 아니라, AI 의 답을 검증하고 "왜 이게 되는지" 를 이해해 자기 것으로 만드는 사람이에요. 기본기는 그 판단력의 바탕이라, AI 시대에 약점이 아니라 오히려 더 강한 무기가 돼요.
🎯 면접관을 홀리는 핵심 멘트
"AI 가 코드를 빠르게 만들어 줄수록, 그 코드가 옳은지 읽고 검증하는 능력이 더 중요해진다고 생각합니다. AI 는 그럴듯한 답을 내놓지만 그게 우리 상황에 맞는지, 동시성 함정이나 메모리 누수가 없는지는 판단하지 못합니다. 그 판단의 바탕이 기본기입니다. 저는 AI 를 답을 베끼는 도구가 아니라, 그 답을 검증하고 '왜 되는지' 를 이해해 제 것으로 만드는 학습 도구로 씁니다. 그래서 기본기를 단단히 다지는 데 시간을 들였습니다."
주제 3. 나는 어떤 개발자로 성장하고 싶은가 — 넓게 vs 깊게
[문제 상황 요약]
앞으로 배울 게 정말 많아요(spring-boot·데이터베이스·인프라·프론트·AI…). 한 사람이 다 깊게 알 순 없어요. 넓게 알아야 할까요, 깊게 파야 할까요?
[튜터의 가이드 및 해설]
흔히 말하는 답은 "T 자형 인재" 예요. T 의 세로획은 한 분야의 깊이, 가로획은 주변 분야의 넓이예요. 한 분야는 깊게 파되, 그 주변을 넓게 이해해서 다른 분야와 협업할 수 있는 사람이죠.
그런데 지금 막 출발선에 선 신입에게는 순서가 중요해요. 처음부터 가로획(넓이)을 욕심내면, 어느 것도 제대로 못 하는 상태가 되기 쉬워요. 그래서 신입 때는 먼저 한 분야의 세로획을 어느 정도 내리는 게 좋아요. 우리에게 그 첫 세로획은 자바·백엔드예요. 한 분야를 깊이 파 본 경험이 있으면, "깊이 안다는 게 어떤 느낌인지" 를 알게 되고, 그 감각이 다음 분야를 배울 때도 그대로 도움이 돼요.
넓이는 그다음에 자연스럽게 따라와요. 백엔드를 하다 보면 데이터베이스가 필요해지고, 배포가 필요해지고, 프론트와의 협업이 필요해져요. 그때마다 한 칸씩 넓히면 돼요. 정답은 사람마다 다르지만, "먼저 하나를 깊게, 그다음 필요에 따라 넓게" 가 신입에겐 든든한 출발이에요.
🎯 면접관을 홀리는 핵심 멘트
"저는 T 자형 개발자로 성장하고 싶습니다. 다만 신입 단계에서는 넓이보다 깊이를 먼저 챙기는 게 맞다고 봅니다. 처음부터 여러 분야를 얕게 건드리면 어느 것도 제대로 못 하기 쉬워서, 우선 자바·백엔드라는 한 세로획을 깊게 내려 '깊이 안다' 는 감각을 익히려 합니다. 그 경험이 다음 분야를 배우는 속도와 깊이로 이어진다고 생각합니다. 넓이는 실무에서 데이터베이스·배포·협업이 필요해질 때마다 한 칸씩 넓혀 가겠습니다."