Java 시리즈 목차: 기초 문법부터 컬렉션, 람다·스트림, 멀티스레드, Spring Boot까지 10편

이 글의 핵심

Java를 배우기 시작하면 문법, 컬렉션, 스트림, 스레드, Spring 중 어디까지 먼저 알아야 하는지 막막합니다. 이 목차는 각 편이 다루는 범위와 선행 관계, 목적별 읽는 순서, 시리즈를 마친 뒤 이어 볼 글을 정리하고, JDK와 JRE의 차이나 Kotlin과의 선택 같은 자주 나오는 질문에도 짧게 답합니다.

시리즈 소개

이 시리즈는 프로그래밍을 처음 배우거나 다른 언어에서 넘어온 사람이 Java로 Spring Boot REST API 서버를 띄울 수 있을 때까지를 열 편으로 다룹니다. 언어 문법(#01~#03), 데이터를 다루는 표준 라이브러리(#04~#06), 프로그램이 바깥 세계와 만나는 부분(#07~#09), 그리고 서버(#10) 순서입니다.

Java를 처음 배울 때 막히는 곳은 대부분 문법 자체가 아니라 “왜 이렇게 써야 하는가”입니다. int와 Integer가 따로 있는 이유, ==로 문자열을 비교하면 안 되는 이유, List<int>를 쓸 수 없는 이유는 모두 기본 타입과 참조 타입이 나뉘어 있다는 한 가지 설계에서 나옵니다. 그래서 이 시리즈는 #02에서 이 구분을 먼저 확실히 다지고, 그 위에 클래스(#03), 제네릭 컬렉션(#04)을 쌓는 순서로 구성했습니다. #02를 건너뛰고 컬렉션이나 Spring으로 바로 가면 Integer 두 개를 ==로 비교했더니 128부터 결과가 달라지는 것 같은 문제를 만났을 때 원인을 찾기 어렵습니다.

실습 환경

예제는 LTS 버전인 Java 17 이상을 기준으로 하며, 가능하면 21을 권합니다. Spring Boot 3가 Java 17 이상을 요구하고, record(#03·#04)와 가상 스레드(#09)처럼 본문에서 소개하는 기능도 이 버전대에서 쓸 수 있습니다. JDK는 OpenJDK 기반 무료 배포판(Eclipse Temurin 등)이면 충분하고, 배포판 사이의 차이와 IntelliJ·Eclipse·VS Code 설정은 #01에서 다룹니다.


편별 안내

언어의 뼈대: #01~#03

  • #01 Java 시작하기: JDK·JRE·JVM의 차이, 운영체제별 JDK 설치, javac로 컴파일해 실행하는 과정, IDE 설정, 기본 문법과 메서드·클래스·배열을 한 번에 훑습니다. 버전별로 추가된 기능(record, switch 패턴 매칭 등)을 표로 정리해 두어 오래된 자료와 비교할 때 참고할 수 있습니다.
  • #02 변수와 타입: 기본 타입과 참조 타입, 형변환, 래퍼 클래스와 박싱, Java 10의 var를 다룹니다. 시리즈 전체에서 가장 먼저 다져야 할 편입니다.
  • #03 클래스와 객체: 생성자와 접근 제어자, 상속과 오버라이딩, 인터페이스와 추상 클래스, 캡슐화를 다루고, 인자가 많아질 때 빌더나 record를 검토하는 기준도 짚습니다.

데이터 다루기: #04~#06

  • #04 컬렉션: ArrayList가 기본값인 이유, HashSet과 HashMap의 동작을 좌우하는 equals/hashCode, 순서나 정렬이 필요할 때의 LinkedHashMap·TreeMap을 다룹니다. 순회 중 삭제로 나는 ConcurrentModificationException과 불변 리스트의 UnsupportedOperationException도 여기서 봅니다.
  • #05 스트림: 스트림 생성, 중간 연산과 최종 연산, collect, 병렬 스트림을 다룹니다.
  • #06 람다: 람다 표현식, Predicate·Function·Consumer 같은 함수형 인터페이스, 메서드 참조(::)를 다룹니다.

번호는 스트림이 먼저지만, 람다 문법이 처음이라면 #06을 먼저 읽는 편이 수월합니다. filter, map, sorted에 넘기는 인자가 모두 람다나 메서드 참조이기 때문입니다. 반대로 람다를 이미 안다면 #05에서 스트림의 지연 평가(최종 연산이 호출되기 전까지 중간 연산이 실행되지 않음)와 “스트림은 한 번만 소비할 수 있다”는 규칙을 먼저 익혀 두면, IllegalStateException: stream has already been operated upon or closed 같은 오류를 만났을 때 바로 원인을 알 수 있습니다.

프로그램 바깥과 만나기: #07~#09

  • #07 예외 처리: 예외 계층, checked와 unchecked 예외의 차이, throws로 전파하기, 커스텀 예외, try-with-resources, 흔한 실수와 성능 고려까지 다룹니다.
  • #08 입출력: BufferedReader·BufferedWriter로 파일을 읽고 쓰는 전통적인 방식과 Java 7의 NIO.2(Path, Files)를 비교합니다.
  • #09 멀티스레드: Thread와 Runnable, ExecutorService로 스레드 풀에 작업 맡기기, synchronized와 ReentrantLock, AtomicInteger, CompletableFuture를 다루고, Java 21 가상 스레드가 어떤 작업에 맞는지 짧게 비교합니다.

서버로: #10

  • #10 Spring Boot: 프로젝트 생성부터 @RestController, Spring Data JPA 리포지토리, 서비스 계층과 @Transactional까지 H2 메모리 DB로 REST API를 만듭니다. 인증(Spring Security), 입력 검증, 테스트, 배포는 이 편의 범위 밖이라 Spring Boot로 REST API 만들기에서 이어집니다.

목적별 읽는 순서

목적순서이어서 볼 글
첫 프로그래밍 언어로 Java#01 → #02 → #03 → #04 → #07콘솔 프로그램을 직접 만들어 보며 #05·#06으로
다른 언어 경험자, 서버가 목표#02 → #04 → #06 → #05 → #07 → #10Spring Boot로 REST API 만들기
코딩 테스트 대비#02 → #04 → #06 → #05알고리즘 시리즈
동시성·성능#07 → #09Java Virtual Thread로 동시성 코드 바꾸기, JVM 깊이 보기

다른 언어를 이미 안다면 #01과 #03은 훑어보고 넘어가도 되지만, #02의 기본 타입·참조 타입 구분과 #04의 equals/hashCode 규칙은 다른 언어와 다르게 동작하는 부분이라 건너뛰지 않는 것을 권합니다.


학습하면서 자주 막히는 지점

첫째는 참조 비교와 값 비교입니다. ==는 두 참조가 같은 객체인지를 비교하므로, 문자열이나 Integer 같은 객체의 내용을 비교할 때는 equals()를 써야 합니다. Integer는 -128~127 범위를 캐시하기 때문에 작은 숫자로 테스트하면 ==가 우연히 맞아 버려서, 운영 데이터에서만 틀리는 버그가 생기기 쉽습니다. 같은 이유로 HashMap의 키로 쓸 클래스를 만들 때 equals()만 재정의하고 hashCode()를 빠뜨리면 get()이 값을 찾지 못합니다. #03과 #04를 이어서 읽어야 하는 이유입니다.

둘째는 checked 예외입니다. 파일을 읽는 코드를 쓰면 unreported exception IOException; must be caught or declared to be thrown 같은 컴파일 오류가 먼저 나오는데, 다른 언어에는 없는 규칙이라 처음에는 모든 곳에 throws Exception을 붙이거나 빈 catch로 삼키는 습관이 생기기 쉽습니다. 람다 안에서는 checked 예외를 그대로 던질 수 없어서 #06과 #07, #08이 서로 얽힙니다.

셋째는 스레드 안전성입니다. ArrayList나 HashMap을 여러 스레드가 동시에 수정하면 예외 없이 데이터가 조용히 깨지거나, 순회 중 수정으로 ConcurrentModificationException이 납니다. Spring의 빈은 기본적으로 싱글톤이라 여러 요청이 같은 객체를 공유하므로, #10에서 컨트롤러나 서비스에 요청별 상태를 필드로 두면 안 되는 이유도 #09의 내용에서 나옵니다.


관련 시리즈

  • Kotlin 시리즈: 같은 JVM 위의 언어입니다. Java의 NullPointerException과 보일러플레이트를 겪어 본 뒤 읽으면 Kotlin의 null 안전성과 데이터 클래스가 무엇을 해결하는지 잘 보입니다. Kotlin으로 Spring Boot 쓰기는 #10과 같은 서버를 Kotlin으로 만드는 편입니다.
  • 알고리즘 시리즈: 코딩 테스트를 Java로 준비한다면 #04~#06과 함께 봅니다.

자주 묻는 질문

JDK, JRE, JVM은 어떻게 다른가요?

JVM은 바이트코드를 실행하는 가상 머신, JRE는 JVM과 표준 라이브러리를 묶은 실행 환경, JDK는 여기에 javac 컴파일러와 jshell, jar 같은 개발 도구를 더한 것입니다. Java 11부터 Oracle은 별도 JRE 배포를 중단해서, 지금은 개발자는 JDK를 설치하고 배포할 때는 jlink로 필요한 모듈만 담은 런타임을 만들거나 컨테이너 이미지에 JDK/JRE 이미지를 쓰는 방식이 일반적입니다.

Java와 Kotlin 중 무엇을 배워야 하나요?

Android 앱이 목표라면 Google이 Kotlin을 우선 언어로 정했으므로 Kotlin이 자연스럽습니다. 서버 개발이라면 기존 코드와 자료가 많은 Java로 시작해도 좋고, 두 언어는 같은 JVM 위에서 서로의 라이브러리를 그대로 쓸 수 있어 나중에 옮기는 비용이 크지 않습니다.

람다와 스트림은 왜 중요한가요?

현대 Java 코드와 Spring 코드의 상당 부분이 람다와 스트림으로 쓰여 있어서, 읽을 수 있어야 다른 사람의 코드를 따라갈 수 있습니다. 다만 모든 반복문을 스트림으로 바꾸는 것이 목표는 아닙니다. parallelStream()도 작은 데이터나 I/O가 섞인 작업에서는 오히려 느려질 수 있어, 측정 없이 켜지 않는 편이 좋습니다.


추천 학습 자료

  • Dev.java: Oracle이 운영하는 공식 학습 사이트로, 최신 버전 기준의 튜토리얼이 있습니다.
  • Java SE API 문서: 컬렉션·스트림·동시성 API를 찾아볼 때 씁니다.
  • Spring 공식 가이드: #10 이후 Spring Boot 기능별 짧은 실습이 모여 있습니다.
  • Joshua Bloch, Effective Java (3판): 시리즈를 마친 뒤 equals/hashCode, 예외, 제네릭을 설계 관점에서 다시 볼 때 좋습니다.

시작하기: Java 시작하기 (#01)부터 읽으면 됩니다.

같이 보면 좋은 글