[TIL 5일 차] 객체지향 프로그래밍의 이해
오늘의 학습
1. 내부 클래스와 익명 클래스
1-01. 내부 클래스(Inner Class)
자바에서 내부 클래스란 클래스 내부에 선언된 또 다른 클래스를 말함
외부 클래스의 멤버(필드/메서드)에 자유롭게 접근할 수 있어, 두 클래스가 긴밀히 협력해야 할 때 밀접하게 관련된 기능을 함께 묶어 설계할 때 사용
class Outer { // 외부 클래스
class Inner {
// 인스턴스 내부 클래스
}
}
1) 내부 클래스를 사용하는 이유
- 외부 클래스와 밀접한 기능을 묶음 → 응집도 증가
- 캡슐화(정보 은닉) 강화
- 가독성 증가 및 유지보수 향상
2) 내부 클래스 종류
| 구분 | 선언 위치 | 외부 클래스 접근 가능 여부 | 특징 |
|---|---|---|---|
| 인스턴스 내부 클래스 | 외부 클래스의 멤버 위치 | 외부 인스턴스 변수 접근 가능 | 외부 클래스와 강한 결합 |
정적(static) 내부 클래스 |
외부 클래스의 멤버 위치 | 외부의 static 멤버만 접근 가능 |
static 키워드 사용, 독립성 높음 |
| 지역 내부 클래스 | 외부 클래스의 메서드 또는 초기화 블록 내부 | final 또는 effectively final 지역 변수 접근 가능 |
메서드 내에서만 사용 |
| 익명 내부 클래스 | 클래스 정의와 인스턴스 생성을 동시에 | 외부 클래스 멤버 접근 가능 | 이름 없는 일회성 클래스 |
1-02. 익명 클래스(Anonymous Class)
이름이 없는 클래스로, 일회성으로 사용될 클래스를 정의할 때 유용하며, 주로 인터페이스나 추상 클래스의 구현체를 즉석에서 정의할 때 사용됨
짧고 간단한 작업을 빠르게 구현할 수 있는 도구로, 이벤트 처리나 콜백 처리, 임시 구현 등에서 자주 활용
1) 익명 클래스의 기본 형식
인터페이스타입 변수 = new 인터페이스타입() {
// 추상 메서드 구현
};
- 또는 추상 클래스의 경우:
추상클래스 변수 = new 추상클래스() {
// 추상 메서드 구현
};
2) 익명 클래스의 한계 및 주의점
- 단 한 번만 사용되며 재사용이 어려움
- 내부에서는 외부 메서드의 지역 변수에 접근할 수 있지만, 해당 지역 변수는 반드시 final 또는 effectively final이어야 함
- 여러 개의 메서드가 있는 인터페이스/추상 클래스는 구현 코드가 길어질 수 있어 가독성이 떨어질 수 있음
2. Enum (열거 타입)의 활용
2-01. Enum 핵심 원리
Enum (enumerated type; 열거형)은 여러 상수들을 보다 편리하게 선언하기 위한 문법 요소로, 내부적으로 클래스로 변환되어 동작하기 때문에 필드, 메서드, 생성자를 모두 가질 수 있다.
- 상수 집합
- 다만 필드가 있다면 생성자를 통해서 값을 주입하여 필드 불변성 유지
- 싱글톤처럼 동작하기에
==비교 가능
1) 기본 형태
public enum OrderStatus {
READY,
IN_PROGRESS,
DONE // 메서드가 추가될 경우 `;` 필요
}
public class Main {
public static void main(String[] args) {
OrderStatus status = Orderstatus.READY;
}
}
- 생성자 추가 시 접근 제한자는
private
2) 필요성
(1) Enum 미사용 시 문제점 (정수/문자열 상수 사용)
public static final int READY = 0;
int status = 0; // 999를 넣어도 컴파일 오류 발생하지 않음
- 말도 안되는 값이 들어와도 컴파일러가 막지 못해 타입 안전성(type safety)이 없음
- 값의 의미를 바로 알 수 없어 유지보수에 취약
- 다른 그룹의 동일한 상수 값의 충돌로 로직 오류 발생
(2) Enum 사용 시 장점
- 상수들이 하나의 타입으로 묶여, 타입 안전성(Type Safty)를 제공
- IDE 자동완성 기능 극대화
- 상수 이름만으로 의미가 명확해져, 코드 가독성이 높아짐
2-02. Enum과 switch문
1) 기존 if-else 기반 분기(문자열 비교) 방식의 문제점
| 문제점 | 설명 |
|---|---|
| 오타 위험 | “ADNIM”처럼 오타가 나도 런타임 전까지 컴파일 오류를 알 수 없음 |
| IDE 지원 약함 | 문자열에 대한 자동완성이 지원되지 않아 실수 쉬움 |
| 타입 안전성 부족 | 어떤 문자열이든 파라미터로 들어올 수 있어서 검증 로직이 복잡해짐 |
| 유지보수 어려움 | 값(문자열)이 변경될 경우 프로젝트 전체의 문자열을 수정해야 함 |
2) switch 기반 분기 방식의 장점
Enum은 switch문과 결합했을 때 가독성이 가장 좋다. JDK 12 이상부터는 Switch Expression을 사용하여 더욱 간결히 표현할 수 있다.
| 장점 | 이유 |
|---|---|
| 컴파일러 보호 | Enum 타입이 아닌 값이 들어오면 컴파일 오류 발생 |
| 오타 불가 | IDE 자동완성으로 상수만 선택 가능 |
| 가독성 향상 | 조건이 명확하고 직관적 |
| 유지보수 용이 | 상수를 변경해도 IDE가 바로 안내 |
2-03. Enum 사용 시 주의사항(Anti-pattern 예방)
잘못 사용하면 유지보수 비용이 커지거나 런타임 오류 발생 가능한 문제
1) ordinal()은 비즈니스 로직에 사용하지 말 것
ordinal()은 상수가 선언된 순서를 정수로 반환
- 문제점
- 누군가 Enum 상수 순서를 변경하거나 새로운 상수를 중간에 추가하면 모든 비즈니스 로직과 저장된 데이터의 의미가 바뀜
- 컴파일러가 아무 경고도 주지 않음
- 해결책
- 상수에 연결된 값은
ordinal()메서드 대신 인스턴스 필드에 저장을 권고
- 상수에 연결된 값은
2) DB 저장 시 name() 대신 고유 코드 사용
- 문제점
- 만약 Enum 이름을 변경하면? → DB에 저장된 값과 매핑 불가능
- 과거 데이터에 대한 호환성 깨짐
- 해결책
- DB에는 name()이 아니라 개발자가 정의한 code 값을 저장하는 것이 안전 (예: “RD”, “IP”, “DN”)
3) Enum 상수 이름을 외부 입력값에 대한 방어적 설계
- 문제점
- 외부 API 요청으로 받은 문자열을
valueOf()로 직접 변환하면 대소문자가 불일치하거나 잘못된 값 입력 시IllegalArgumentException발생으로 서비스가 중단
- 외부 API 요청으로 받은 문자열을
- 해결책
- 안전한 변환 메서드 제공
2-04. Enum을 사용할 때 피해야 하는 반패턴(Anti-pattern)
Enum을 잘못 사용한 사례
1) 모든 Enum을 한 파일에 몰아넣기
- 문제점
- 책임 분리 실패
- 파일이 지나치게 커짐
- 협업 시 충돌 잦음
- 해결책
- Enum은 도메인별로 분리합니다.
2) Enum에 과도한 로직 삽입
- 문제점
- Enum 내부에서 DB 조회하거나 외부 서비스(API)를 직접 호출하는 것은 결합도를 높임
- 해결책
- Enum은 경량 구조여야 하며,
- DB 호출, 외부 API 호출 등은 서비스 계층에서 처리해야 합니다.
3) Enum을 문자열처럼 사용하기
- 문제점
String input = “READY”;Status.valueOf(input)→ 예외 발생 위험
- 해결책
- 문자열이나 정수 값을 Enum 으로 안전하게 변환해주는 메서드를 내부에 구현 (
fromCode등)
- 문자열이나 정수 값을 Enum 으로 안전하게 변환해주는 메서드를 내부에 구현 (
⭐⭐3. 예외 처리(Exception Handling)
3-01. 예외(Exception)의 개념과 필요성
자바에서 발생하는 문제 상황은 오류(Error)와 예외(Exception)으로 나뉨
- 오류(Error) : 시스템 레벨에서의 심각한 문제로, 개발자가 복구할 수 없어 처리하지 않는 것이 일반적
- 예외(Exception) : 프로그램 실행 중 발생할 수 있는 예상 가능한 문제로, 개발자가 적절히 처리
| 구분 | Error | Exception |
|---|---|---|
| 의미 | 시스템 레벨에서 발생하는 치명적 문제 | 개발자가 처리할 수 있는 문제 |
| 예시 | 메모리 부족(OutOfMemoryError), 스택 오버플로(StackOverflowError) |
NullPointerException, IllegalArgumentException |
| 복구 가능 여부 | 거의 불가능 | 대응 로직을 통해 비교적 복구 가능 |
| 처리 방법 | JVM 차원 처리(프로그램 종료) | 개발자가 try-catch 등으로 예외 상황 처리 |
1) 예외 처리의 중요성
파일이 존재하지 않거나, 네트워크 연결이 실패하거나, 사용자의 잘못된 입력, DB 연결 문제 등 외부 요인에 의해 발생하는 문제 상황은 개발자가 완벽히 막을 수 없다. 적절한 예외 처리가 없다면 프로그램은 즉시 종료됨
- 예외 처리의 주요 역할
- 프로그램 안정성 : 예기치 못한 종료 방지해 서비스 연속성 유지
- 사용자 경험 향상 : 에러 코드에 대한 친절한 안내 메시지 제공
- 에러 로깅 : 문제 원인을 기록하여 사후 분석
- 예외 처리 시 원칙
- 예외의 원인은 명확하게 기록
- ⭐예외로 프로그램 흐름을 제어하지 않기
- 예외 구조를 이해하면 나중에 Spring 예외 처리도 연결됨
- Error는 절대 잡지 않기
- 파일이나 네트워크 연결은 반드시 닫아야 함 (
try-with-resources활용)
2) 자바의 예외 계층 구조

- Throwable : 자바에서 던질(
throw) 수 있는 모든 객체의 최상위 부모 - Error : 시스템 단에서 발생하는 치명적 오류이며 개발자가 처리하지 않음
- Exception : 개발자가 처리 가능한 모든 예외의 부모 클래스
- RuntimeException : 실행 중(Runtime) 자주 발생하는 예외들의 부모로, 주로 개발자의 논리적 실수에서 시작됨
⭐3) RuntimeException과 비 RuntimeException의 개념적 차이
| 구분 | RuntimeException | 그 외 Exception |
|---|---|---|
| 발생 시점 | 실행 중(Runtime) | 컴파일 단계에서 감지 |
| 처리 여부 | 선택적(Unchecked Exception) | 반드시 처리 필요(Checked Exception) |
| 대표 예외 | NullPointerException, IllegalArgumentException |
IOException, SQLException |
3-02. 체크드 예외 (Checked Exception)와 언체크드 예외 (Unchecked Exception)
1) 체크드 예외 (Checked Exception)
컴파일 시점에 반드시 처리해야 하는 예외로, 처리하지 않으면 컴파일 에러가 발생합니다. IDE에서 뜨는 빨간줄 중 일부는 체크드 예외 처리를 안 해서 생긴 컴파일 에러라고 볼 수 있다.
즉, 컴파일러가 “너 이 예외 발생할 수도 있는데 어떻게 처리할 거야?”라고 묻는 예외로, try-catch로 잡거나 throws로 던져야 한다.
대표 예시: IOException, SQLException, FileNotFoundException
- 발생 이유: 외부 시스템(파일, 네트워크, DB 등)과의 상호작용에서 발생하는 불확실성
- 대표 발생 주체: JVM 외부 시스템
- 실무 활용: 안정성 보장을 위해 필요한 경우 사용
- 특징: 프로그램 외부 환경에 의한 실패로, 실패를 복구할 방법이 있을 때 사용
2) 언체크드 예외(Unchecked Exception)
컴파일러가 처리 여부를 강제하지 않는 예외로, try-catch를 쓰지 않아도 컴파일되지만, 실행 중(Runtime)에 실패함
주로 RuntimeException을 상속받는 예외들이다.
대표 예시: NullPointerException, IllegalArgumentException, IndexOutOfBoundsException
- 발생 이유: ⭐주로 개발자의 실수 또는 비즈니스 규칙 위반 시 발생
- 대표 발생 주체: 코드 내부 논리
- 실무 활용: 일반적으로 선호됨(가벼운 검증 실패, 잘못된 파라미터 전달, 프로그래밍 오류 표현)
- 특징: 굳이 모든 곳에 예외 처리 코드를 적지 않아도 되기에 코드가 간결해짐
4. UML을 활용한 객체 지향 설계 시각화
4-01. UML(Unified Modeling Language)
시스템을 시각적으로 표현하기 위한 표준화된 모델링 언어로, 문서화나 기획할 때 필요한 도구
개발자에게는 “설계 지도”라고 이해하면 쉽다.
1) UML의 필요성
- 복잡한 시스템의 시각적인 이해를 도움
- 팀 간 소통 및 협업 향상에 도움
- 설계 오류를 조기에 발견하는데 도움
- 유지보수에 도움
2) UML의 목적
- 구조 시각화 : 클래스, 객체, 패키지 등 시스템의 구성 요소를 표현
- 동작 시각화 : 시퀀스, 상태, 활동 등 객체 간 상호작용을 표현
- ⭐명세화(Specification) : 개발자 간 “이 기능은 이렇게 동작한다”라는 공통 이해를 위해 동작 및 구조를 명확히 정의
- ⭐문서화(Documentation) : 시간이 지난 뒤에도 시스템의 구조를 파악할 수 있어 유지보수와 협업을 위한 설계 문서 역할을 함
4-02. UML의 종류
크게 구조 다이어그램과 행위 다이어그램으로 나뉨
1) 구조 다이어그램 (Static Diagram)
- ⭐클래스 다이어그램
- 객체 지향 시스템의 정적 구조를 시각적으로 표현하는 UML 다이어그램
- 클래스(Class), 속성(Attribute), 메서드(Operation), 그리고 객체 간의 관계(Relationship)를 표현
- 객체 다이어그램
- 패키지 다이어그램
- 컴포넌트 다이어그램
2) 행위 다이어그램 (Behavior Diagram)
- ⭐시퀀스 다이어그램
- 시간의 흐름에 따라 객체 간에 오가는 메시지(메서드 호출)를 표현하는 UML 다이어그램
- 특정 기능이 어떤 순서로 진행되는지 파악할 때 사용
- 활동(Activity) 다이어그램
- 상태(State) 다이어그램
4-03. UML 작성 툴
| 도구 | 방식 | 설치 여부 | 협업 | 장점 | 단점 |
|---|---|---|---|---|---|
| Mermaid | 코드 기반 | 불필요 | 우수 (Notion, GitHub 지원) | 빠른 작성, 버전 관리 쉬움 | 복잡한 다이어그램은 문법 난도 ↑ |
| Draw.io | 드래그&드롭 GUI | 불필요 | 보통 | 직관적, 시각화 강력 | 버전 관리 어려움 |
| PlantUML | 코드 기반 | 필요 (or 서버) | 우수 | 복잡한 다이어그램 표현력 최고 | 설정 난이도 ↑ |
Leave a comment