Java를 실무 흐름으로 이해하기
엔터프라이즈 표준 언어 Java를 학습합니다. JVM, 객체지향, 컬렉션, 예외 처리, 동시성, Stream API, Spring Boot까지 실무에 필요한 것을 다룹니다. 이 가이드는 개념을 나열하기보다, 실제 프로젝트에서 판단해야 하는 순서대로 내용을 따라갈 수 있게 구성했습니다.
엔터프라이즈 표준 언어 Java를 학습합니다. JVM, 객체지향, 컬렉션, 예외 처리, 동시성, Stream API, Spring Boot까지 실무에 필요한 것을 다룹니다.
엔터프라이즈 표준 언어 Java를 학습합니다. JVM, 객체지향, 컬렉션, 예외 처리, 동시성, Stream API, Spring Boot까지 실무에 필요한 것을 다룹니다. 이 가이드는 개념을 나열하기보다, 실제 프로젝트에서 판단해야 하는 순서대로 내용을 따라갈 수 있게 구성했습니다.
문법보다 요청이 들어와 검증, 처리, 저장, 응답으로 이어지는 경계를 먼저 잡습니다.
글로 읽은 내용을 머릿속에 오래 남기려면 먼저 흐름을 그림으로 잡는 편이 좋습니다. 아래 두 그림은 Java를 학습할 때 계속 되돌아볼 수 있는 기준 지도입니다.
Java를 처음 펼칠 때는 세부 명령보다 큰 그림이 먼저입니다. 이 섹션에서는 앞으로 배울 개념들이 어떤 문제를 풀기 위해 등장했는지부터 잡아봅니다.
# SDKMAN 설치
curl -s "https://get.sdkman.io" | bash
sdk install java 21.0.2-tem
sdk use java 21.0.2-tem
java -version여기서는 기본 문법을 실제 코드와 함께 확인합니다. 예제를 그대로 따라 하기보다, 입력과 출력, 그리고 바뀌기 쉬운 부분이 어디인지 보면서 읽어보세요.
// Record (불변 데이터 클래스)
record Point(double x, double y) {
double distanceTo(Point other) {
return Math.sqrt(Math.pow(x - other.x, 2) + Math.pow(y - other.y, 2));
}
}
// Pattern Matching
Object obj = "Hello";
if (obj instanceof String s && s.length() > 3) {
System.out.println(s.toUpperCase());
}여기서는 객체지향 프로그래밍을 실제 코드와 함께 확인합니다. 예제를 그대로 따라 하기보다, 입력과 출력, 그리고 바뀌기 쉬운 부분이 어디인지 보면서 읽어보세요.
// 제네릭 + 인터페이스
public interface Repository<T, ID> {
Optional<T> findById(ID id);
List<T> findAll();
T save(T entity);
void deleteById(ID id);
}여기서는 컬렉션 프레임워크을 실제 코드와 함께 확인합니다. 예제를 그대로 따라 하기보다, 입력과 출력, 그리고 바뀌기 쉬운 부분이 어디인지 보면서 읽어보세요.
List<String> names = new ArrayList<>(List.of("Ada", "Linus", "Grace"));
Set<String> unique = new HashSet<>(names); // 중복 제거, 순서 보장 안 함
Map<String, Integer> ageByName = new HashMap<>();
ageByName.put("Ada", 30);
// 삽입 순서를 유지해야 한다면
Map<String, Integer> ordered = new LinkedHashMap<>();
// 정렬된 상태를 유지해야 한다면
Set<Integer> sorted = new TreeSet<>(List.of(5, 3, 8, 1)); // [1, 3, 5, 8]| 자료구조 | 특징 | 대표 구현체 |
|---|---|---|
| List | 순서 있음, 중복 허용, 인덱스로 접근 | ArrayList (빠른 조회), LinkedList (빠른 삽입/삭제) |
| Set | 중복 불허 | HashSet (순서 없음), LinkedHashSet (입력 순서), TreeSet (정렬) |
| Map | 키-값 쌍 | HashMap (순서 없음), LinkedHashMap (입력 순서), TreeMap (키 정렬) |
여기서는 예외 처리을 실제 코드와 함께 확인합니다. 예제를 그대로 따라 하기보다, 입력과 출력, 그리고 바뀌기 쉬운 부분이 어디인지 보면서 읽어보세요.
// try-with-resources — 블록이 끝나면 자동으로 close() 호출
try (BufferedReader reader = new BufferedReader(new FileReader("data.txt"))) {
String line = reader.readLine();
} catch (IOException e) {
throw new RuntimeException("파일 읽기 실패", e); // 원인(e)을 반드시 함께 감싸서 던지기
}
// 커스텀 예외 — 의미 있는 이름으로 실패 상황을 표현
class InsufficientBalanceException extends RuntimeException {
public InsufficientBalanceException(String message) {
super(message);
}
}
public void withdraw(int amount) {
if (amount > balance) {
throw new InsufficientBalanceException("잔액 부족: 요청 " + amount + ", 잔액 " + balance);
}
balance -= amount;
}| 구분 | 예시 | 처리 강제 여부 |
|---|---|---|
| Checked Exception | IOException, SQLException | 컴파일러가 catch 또는 throws 선언을 강제 |
| Unchecked Exception | NullPointerException, IllegalArgumentException | 강제하지 않음 — 프로그래밍 오류를 나타내는 경우가 많음 |
여기서는 Stream & Lambda을 실제 코드와 함께 확인합니다. 예제를 그대로 따라 하기보다, 입력과 출력, 그리고 바뀌기 쉬운 부분이 어디인지 보면서 읽어보세요.
import java.util.stream.*;
var names = List.of("Alice", "Bob", "Charlie", "Dave");
var result = names.stream()
.filter(n -> n.length() > 3)
.map(String::toUpperCase)
.sorted()
.collect(Collectors.toList());
// [ALICE, CHARLIE, DAVE]여기서는 동시성 & 멀티스레딩을 실제 코드와 함께 확인합니다. 예제를 그대로 따라 하기보다, 입력과 출력, 그리고 바뀌기 쉬운 부분이 어디인지 보면서 읽어보세요.
// ExecutorService — 스레드 풀로 작업을 관리
ExecutorService executor = Executors.newFixedThreadPool(4);
Future<Integer> future = executor.submit(() -> heavyComputation());
Integer result = future.get(); // 완료될 때까지 대기
executor.shutdown();
// CompletableFuture — 비동기 작업을 파이프라인처럼 조합
CompletableFuture<String> pipeline = CompletableFuture
.supplyAsync(() -> fetchUserId())
.thenApply(id -> fetchUserName(id))
.thenApply(name -> "안녕하세요, " + name)
.exceptionally(ex -> "오류 발생: " + ex.getMessage());
pipeline.thenAccept(System.out::println);여기서는 Spring Boot을 실제 코드와 함께 확인합니다. 예제를 그대로 따라 하기보다, 입력과 출력, 그리고 바뀌기 쉬운 부분이 어디인지 보면서 읽어보세요.
@RestController
@RequestMapping("/api/users")
@RequiredArgsConstructor
public class UserController {
private final UserService userService;
@GetMapping
public List<UserDto> getAll() {
return userService.findAll();
}
@PostMapping
@ResponseStatus(HttpStatus.CREATED)
public UserDto create(@Valid @RequestBody CreateUserRequest req) {
return userService.create(req);
}
}Java 실무 설계은 선택지가 갈리는 지점입니다. 표를 기준으로 각 방법의 쓰임새와 운영상의 차이를 비교해두면 이후 판단이 훨씬 쉬워집니다.
| 결정 지점 | 확인 질문 | 실무 기준 |
|---|---|---|
| 경계 | Java 코드에서 바뀌기 쉬운 부분은 어디인가? | 입출력, 설정, 외부 연동, 핵심 규칙을 분리합니다. |
| 상태 | 상태가 어디서 생성되고 어디서 사라지는가? | 상태 소유자와 수명 주기를 코드로 드러냅니다. |
| 장애 | 실패했을 때 호출자는 무엇을 받는가? | timeout, fallback, error contract를 먼저 정합니다. |
이 섹션은 Java 운영 기준을 실무 관점에서 정리합니다. 개념을 외우기보다, 어떤 상황에서 이 기준을 꺼내 쓸지에 초점을 맞춰보세요.
Java 검증 전략은 선택지가 갈리는 지점입니다. 표를 기준으로 각 방법의 쓰임새와 운영상의 차이를 비교해두면 이후 판단이 훨씬 쉬워집니다.
| 품질 축 | 검증 방법 | 완료 기준 |
|---|---|---|
| 정확성 | 정상/실패 케이스를 자동화합니다. | 핵심 시나리오가 재현 가능하게 통과합니다. |
| 회귀 방지 | 버그 수정 시 동일 케이스를 테스트로 남깁니다. | 같은 장애가 다시 배포되지 않습니다. |
| 운영성 | 로그, 메트릭, 알림을 확인합니다. | 문제가 생겼을 때 원인 추적 경로가 있습니다. |