Spring Boot (+ RESTful)

RESTful 웹 서비스 구축 - # Project 04 - 이미지 업로드 (9)

wy-family 2025. 1. 11. 13:11

질문 17. 썸네일 이미지와 관련해서, 이미지의 크기 조정을 위해서 getThumbnail 이라는 메서드를 만들었다. 그 과정에서 의존성 주입을 해야 했다. 여기서는 Maven 이 아니라 Gradle 이라서 implementation 'org.imgscalr:imgscalr-lib:4.2' 를 의존성에 추가해주었다. 여기서 질문이, 다른 코드는 입력을 했다고 해서 새로 고침 버튼을 눌러줘야 하거나 그러지 않는데 왜 build.gradle 에다가 의존성을 추가했다고 해서 Gradle 의 새로 고침 버튼을 눌러줘야 하는거야? 그러니까 예를 들어서 java 파일에다가 함수가 하나 추가되었다고 해서 새로 고침 버튼을 눌러주지는 않는데 왜 Gradle 에 의존성을 추가하는건 새로  고침 버튼을 눌러주는거야? 똑같은 IntelliJ 프로그램 내에서 작성하는 것인데 말이야.

 

정말 예리한 질문이에요! 👀
Java 코드를 작성할 때는 새로 고침 버튼을 누를 필요가 없지만,
build.gradle 파일을 수정했을 때는 **Gradle의 새로 고침 버튼(Refresh)**을 눌러줘야 하는 이유가 궁금하신 거죠?

차근차근 설명해드릴게요.


🎯 1. 왜 build.gradle 수정 후 새로 고침이 필요할까?

Java 코드를 수정하는 것과 build.gradle 파일을 수정하는 것은 IntelliJ의 처리 방식이 완전히 다릅니다.

(1) Java 코드를 수정할 때

Java 코드를 수정하면 IntelliJ가 자동으로 코드를 컴파일하고, 프로젝트 내의 변경 사항을 실시간으로 반영합니다.

  • 코드 수정 → IntelliJ가 바로 감지
  • 자동 컴파일 → 변경 사항 바로 적용

즉, Java 코드 변경 사항은 IDE(IntelliJ)가 알아서 처리하기 때문에 새로 고침 버튼을 누를 필요가 없습니다.


(2) build.gradle 파일을 수정할 때

하지만 build.gradle 파일은 Gradle 빌드 도구가 관리하는 설정 파일이에요.

  • Gradle은 프로젝트의 의존성 관리빌드 작업을 처리하는 외부 도구입니다.
  • IntelliJ는 Gradle 빌드 도구와 별도로 동작하기 때문에,
    build.gradle 파일을 수정했을 때 IntelliJ가 바로 인식하지 못합니다.

따라서 build.gradle 파일이 변경되었을 때는 IntelliJ에게 "이제 Gradle 설정이 바뀌었으니, 다시 빌드를 해줘!"라고 알려줘야 합니다.
그 역할을 하는 게 바로 **Gradle의 새로 고침 버튼(Refresh)**입니다.


🧩 2. 차이를 쉽게 이해하는 비유

📋 Java 코드 수정은?

문서를 열어서 내용을 수정하고 바로 저장하는 것과 같아요.
문서 편집기(IntelliJ)가 바로 변경 사항을 인식하고 저장해줍니다.

📋 build.gradle 수정은?

프로그램 설정 파일을 수정하고, 프로그램을 다시 시작해야 적용되는 것과 같아요.
프로그램이 설정 파일이 바뀐 걸 인식하지 못하기 때문에
"새로 고침(Refresh)" 버튼을 눌러서 변경 사항을 적용하라고 알려줘야 합니다.


🛠 3. Gradle의 역할을 다시 이해해보자

Gradle은 의존성 관리, 프로젝트 빌드, 테스트 실행 등을 자동화해주는 빌드 도구입니다.

Gradle이 하는 일

  1. 의존성 관리:
    • 프로젝트에 필요한 라이브러리들을 외부 저장소(Maven Central 등)에서 다운로드하고 관리합니다.
  2. 빌드 관리:
    • 프로젝트의 코드를 컴파일, 패키징, 테스트 등을 자동화합니다.

🧪 예시: 의존성 추가 시 Gradle의 작업

implementation 'org.imgscalr:imgscalr-lib:4.2'
  1. build.gradle 파일에 의존성을 추가하면,
  2. Gradle이 해당 라이브러리를 Maven Central에서 다운로드합니다.
  3. 다운로드한 라이브러리를 프로젝트에 포함시킵니다.

🤔 왜 새로 고침(Refresh)을 해야 할까?

Gradle이 프로젝트에 필요한 라이브러리를 다운로드하고 적용하는 과정은 외부 작업이기 때문에,
IntelliJ는 자동으로 그 작업을 수행하지 않습니다.
그래서 **수동으로 새로 고침(Refresh)**을 눌러서 **"Gradle에게 변경 사항을 적용해줘!"**라고 알려줘야 해요.


📝 4. 정리: 새로 고침이 필요한 경우와 그렇지 않은 경우

변경 사항 새로 고침 필요 여부 이유
Java 코드 수정 ❌ 필요 없음 IntelliJ가 자동으로 변경 사항을 감지하고 적용
build.gradle 수정 ✅ 필요함 Gradle은 IntelliJ와 별도로 동작하기 때문에 수동 새로 고침 필요
리소스 파일 수정 (HTML, CSS) ❌ 필요 없음 IntelliJ가 변경 사항을 바로 적용

💡 5. 실무에서는 어떻게 할까?

실무에서도 **build.gradle 수정 후 항상 새로 고침(Refresh)**을 눌러야 해요.
하지만 IntelliJ 설정에서 Gradle의 자동 새로 고침(Refresh)을 활성화할 수도 있습니다.


🔧 IntelliJ에서 Gradle 자동 새로 고침 설정 방법

  1. File → Settings → Build, Execution, Deployment → Build Tools → Gradle로 이동.
  2. Build and run usingGradle로 설정.
  3. Auto-import 체크박스를 활성화.

🎯 6. 최종 요약

질문 답변
왜 Java 코드는 새로 고침이 필요 없을까? IntelliJ가 변경 사항을 자동으로 감지하고 적용하기 때문.
왜 build.gradle은 새로 고침이 필요할까? Gradle은 외부 빌드 도구이기 때문에, IntelliJ와 별도로 동작하기 때문.
실무에서 Gradle 새로 고침을 자동화할 수 있을까? ✅ 가능함. IntelliJ에서 Gradle 자동 새로 고침 설정을 활성화하면 됨.

 

IntelliJ(Java), Gradle 말고도 개발을 할 때 또 동작하고 있는 도구가 있어?
아주 좋은 질문이에요! 😃

**"IntelliJ, Gradle 외에 개발을 할 때 백그라운드에서 동작하고 있는 도구"**를 물어보셨죠?

결론부터 말하면, 개발 환경에서는 다양한 도구들이 백그라운드에서 동시에 동작하고 있어요.
IntelliJ와 Gradle 외에도 여러 가지 빌드 도구, 런타임, 서버, 패키지 매니저, 데이터베이스 등이 동시에 작동합니다.
이제 구체적으로 어떤 도구들이 있는지 설명할게요.


1. IntelliJ와 함께 동작하는 주요 도구들

🛠 (1) JDK (Java Development Kit)

  • JDKJava 프로그램을 실행하고 컴파일하는 필수 도구입니다.
  • IntelliJ는 코드 작성편집을 도와줄 뿐이고,
    코드를 실제로 컴파일하고 실행하는 건 JDK가 합니다.

📋 JDK가 하는 일

기능 설명
javac Java 소스를 **바이트코드(.class 파일)**로 컴파일
java 컴파일된 바이트코드를 실행
javadoc 코드 문서를 생성

🛠 (2) Gradle (또는 Maven)

  • Gradle빌드 도구로, 프로젝트의 의존성 관리, 컴파일, 테스트, 패키징 등을 자동화합니다.
  • Maven도 비슷한 역할을 하는 빌드 도구입니다.

🛠 (3) Application Server (Spring Boot 내장 서버)

  • 만약 웹 애플리케이션을 개발 중이라면,
    Spring Boot와 같은 프레임워크가 **내장 서버(Tomcat, Jetty 등)**를 실행합니다.

📋 Spring Boot 내장 서버 예시

서버 종류 설명
Tomcat Spring Boot의 기본 내장 서버
Jetty 경량 서버

💡 어떻게 동작할까?

  • IntelliJ에서 Spring Boot 애플리케이션을 실행하면,
    Gradle이 빌드를 하고,
    **내장 서버(Tomcat)**가 백그라운드에서 애플리케이션을 구동합니다.

🛠 (4) Package Manager (npm, yarn 등)

  • 프론트엔드 개발을 할 때는 npm이나 yarn 같은 패키지 매니저도 백그라운드에서 동작합니다.

🛠 (5) Database Server (MySQL, PostgreSQL 등)

  • 백엔드 개발에서는 데이터베이스 서버도 백그라운드에서 작동하고 있습니다.

📋 예시

데이터베이스 종류 설명
MySQL 대표적인 관계형 DB
PostgreSQL 고성능 관계형 DB
MongoDB NoSQL 데이터베이스

🛠 (6) Git (버전 관리 도구)

  • 개발 환경에서는 Git도 백그라운드에서 동작합니다.
  • IntelliJ는 Git과 통합되어 있어,
    코드 변경 사항을 자동으로 추적하고 버전 관리를 할 수 있습니다.

🛠 (7) Linter/Formatter (코드 스타일 도구)

  • Lint 도구코드 포매터도 백그라운드에서 실행됩니다.
  • 코드 스타일 검사자동 포매팅을 도와줍니다.

📋 예시

도구 이름 설명
Checkstyle Java 코드 스타일 검사
SpotBugs Java 코드 버그 탐지
Prettier 프론트엔드 코드 자동 포매터

🧩 2. 현업에서 개발 시 백그라운드에서 동작하는 도구들

도구 종류 설명 예시
JDK Java 코드 컴파일 및 실행 OpenJDK, Oracle JDK
빌드 도구 프로젝트 빌드 및 의존성 관리 Gradle, Maven
서버 애플리케이션 서버 Tomcat, Jetty
패키지 매니저 프론트엔드 패키지 관리 npm, yarn
데이터베이스 서버 데이터 저장소 MySQL, PostgreSQL, MongoDB
버전 관리 도구 코드 변경 사항 추적 및 관리 Git
Linter/Formatter 코드 스타일 검사 및 자동 포매팅 Checkstyle, SpotBugs, Prettier

🎯 3. 최종 요약

IntelliJ, Gradle 외에도 다양한 도구들이 동작 중

  1. JDK: Java 코드를 컴파일하고 실행하는 도구.
  2. Gradle/Maven: 프로젝트의 의존성 관리빌드 자동화.
  3. 내장 서버: Spring Boot 애플리케이션을 실행하는 Tomcat 서버 등.
  4. 패키지 매니저: 프론트엔드 패키지 관리 도구 npm/yarn.
  5. 데이터베이스 서버: MySQL, PostgreSQL 등 데이터베이스 서버.
  6. Git: 코드의 버전 관리를 담당하는 도구.
  7. Linter/Formatter: 코드 스타일 검사자동 포매팅.

질문 18. 썸네일 이미지와 관련해서, getThumbnail 이라는 메서드를 만들었고, MultipartFile originFile, Integer width 를 인자로 받는 메서드야. 그런데, int width 가 아니라 왜 Integer 라고 하는거야? 그리고 가끔 코드를 작성하다보면 long 이 아니라 Long 으로 하는 경우가 있던데 그 이유도 비슷한 이유인건가?

 

좋은 질문이에요! 😊
왜 int 대신 Integer, long 대신 **Long**을 사용하는지 궁금하신 거죠?
이건 **기본 타입(primitive type)**과 **래퍼 클래스(wrapper class)**의 차이와 관련이 있어요.

제가 차근차근 int와 Integer의 차이, 그리고 어떤 상황에서 Integer를 사용해야 하는지 설명해드릴게요.


🔎 1. int vs Integer의 차이

구분 int Integer
타입 기본 타입 (primitive type) 래퍼 클래스 (wrapper class)
메모리 사용 더 적음 더 많음
null 허용 여부 ❌ 허용되지 않음 ✅ 허용됨
사용 목적 단순한 숫자 연산 객체처럼 취급해야 하는 상황

int는 기본 타입

  • int는 숫자 데이터를 직접 저장하는 기본 타입입니다.
  • 메모리를 효율적으로 사용하며, null 값은 허용되지 않습니다.

📋 예시: int 사용

int number = 10;     // 가능
number = null;       // 컴파일 에러 (null을 허용하지 않음)

Integer는 래퍼 클래스

  • Integer는 int를 객체로 감싸는 클래스입니다.
  • 객체처럼 동작하기 때문에, null 값을 허용할 수 있습니다.

📋 예시: Integer 사용

Integer number = 10;  // 가능
number = null;        // 가능 (null 허용)

🧩 2. 왜 Integer를 사용해야 할까?

(1) null 값을 허용해야 하는 경우

📋 예시: 썸네일 크기 설정

public BufferedImage getThumbnail(MultipartFile originFile, Integer width) throws IOException {
    if (width == null) {
        width = 300;  // 기본 크기 설정
    }
    // 썸네일 이미지 생성 로직
}
  • 여기서 width가 null일 수도 있는 상황을 고려해야 합니다.
  • 만약 **int width**로 선언하면 null 값을 허용하지 않기 때문에 예외가 발생합니다.
  • 따라서, Integer로 선언해서 null 값 처리를 안전하게 할 수 있습니다.

(2) 컬렉션 사용 시

Java의 **컬렉션(List, Set, Map 등)**은 객체만 저장할 수 있기 때문에 기본 타입을 사용할 수 없습니다.

📋 예시: List에 숫자 저장

List<Integer> numbers = new ArrayList<>();
numbers.add(10);     // 가능
numbers.add(null);   // 가능

만약 List<int> 같은 코드를 작성하려고 하면 컴파일 에러가 발생합니다.


(3) 제네릭(Generic) 타입 사용 시

Java의 제네릭 타입객체 타입만 허용합니다.

📋 예시: 제네릭 타입 사용

public class MyClass<T> {
    private T value;

    public MyClass(T value) {
        this.value = value;
    }
}

만약 int와 같은 기본 타입을 사용하려면 Integer로 변환해야 합니다.


📝 3. 자동 변환: 오토박싱(Auto-Boxing)과 언박싱(Unboxing)

Java는 기본 타입과 래퍼 클래스 간의 변환을 자동으로 처리합니다.

오토박싱 (Auto-Boxing)

기본 타입 → 래퍼 클래스

int number = 10;
Integer obj = number;  // int가 Integer로 자동 변환

언박싱 (Unboxing)

래퍼 클래스 → 기본 타입

Integer obj = 10;
int number = obj;      // Integer가 int로 자동 변환

🤔 4. 왜 long 대신 Long을 사용하는 경우가 있을까?

long과 Long의 차이도 int와 Integer의 차이와 동일한 이유입니다.

타입 설명
long 기본 타입으로, 숫자 데이터만 저장
Long 객체 타입으로, null 값을 허용

실무에서 Long을 사용하는 경우

  1. 데이터베이스의 PK 값을 처리할 때:
    • PK 값은 null일 수 있는 경우가 있음 (예: 새 레코드를 생성할 때 아직 ID가 없을 때).
    • 따라서 Long으로 선언해야 null 처리가 가능합니다.

📋 예시: PK 값 처리

public class User {
    private Long id;  // null 허용
}

🎯 5. 언제 int와 Integer를 사용해야 할까?

상황 사용 타입 이유
단순한 숫자 연산이 필요할 때 int 메모리 효율이 더 좋고, 성능이 더 뛰어남
null 값을 허용해야 할 때 Integer null 처리가 가능해야 함
컬렉션(List, Set, Map 등)을 사용할 때 Integer 컬렉션은 객체 타입만 허용함
데이터베이스의 PK 값을 처리할 때 Long ID 값이 null일 수 있기 때문에 Long으로 처리해야 함
제네릭 타입을 사용할 때 Integer, Long 제네릭은 객체 타입만 허용함

💡 6. 최종 요약

  • int:
    • 기본 타입.
    • null 값을 허용하지 않음.
    • 단순 숫자 연산에 적합.
  • Integer:
    • 래퍼 클래스.
    • null 값을 허용.
    • 컬렉션, 제네릭, 데이터베이스 처리 시 사용.

 


질문 19. throws IOException 이라고 했는데, try catch 문에서 에러를 잡아내는건, try 를 해보고 나서 에러가 발생하면, e.printTrace 같은 걸 해줘서 어디서 에러가 발생했는지를 알려주기도 하는데, throws 의 경우에는 메서드를 사용했는데 어떤 에러가 발생하면 잡아내는 것으로 이해가 되는데, 이 경우에는 어디서 에러가 발생했는지를 알려주기도 해? try catch 로 에러를 잡는거랑 throws 로 에러를 잡는거랑 공통점과 차이가 뭐야?

 

아주 중요한 질문이에요! 😊
**throws와 try-catch**는 에러(예외)를 처리하는 방식에 있어서 큰 차이가 있어요.

이제 차근차근 공통점과 차이점을 이해할 수 있도록 설명해드릴게요!


🔎 1. throws와 try-catch의 기본 개념

(1) throws란?

  • throws는 메서드 선언부에서 사용하며,
    **"이 메서드는 특정 예외를 던질 수 있으니, 호출하는 쪽에서 예외를 처리해라"**라고 알려주는 역할을 합니다.
public void readFile(String filePath) throws IOException {
    // 파일 읽기 작업
}

📋 예시

readFile("test.txt");  // 이 메서드를 호출하는 쪽에서 IOException을 처리해야 함.

(2) try-catch란?

  • try-catch는 예외가 발생할 수 있는 코드를 감싸고,
    예외가 발생했을 때 그 예외를 직접 처리하는 방식입니다.
try {
    readFile("test.txt");
} catch (IOException e) {
    e.printStackTrace();  // 예외 처리
}

🧩 2. throws와 try-catch의 공통점

공통점 설명
예외 처리와 관련 둘 다 예외(Exception)를 처리하기 위해 사용
예외 유형 지정 가능 처리할 **예외 유형(IOException 등)**을 지정 가능
코드 안정성 향상 둘 다 코드의 안정성을 높이는 역할을 함

🔧 3. throws와 try-catch의 차이점

구분 throws try-catch
예외 처리 위치 메서드를 호출하는 쪽에서 예외를 처리 예외가 발생한 곳에서 직접 처리
코드 흐름 예외가 발생하면 호출 스택을 따라 예외가 던져짐 예외가 발생하면 해당 catch 블록에서 처리
에러 메시지 출력 에러 발생 시 스택 트레이스 자동 출력 e.printStackTrace() 등을 통해 수동 출력
코드 길이 코드가 짧아짐 (예외를 위임하기 때문) 코드가 길어질 수 있음 (예외 처리 코드 포함)

🛠 4. 코드 흐름 비교: throws vs try-catch

🔧 (1) throws를 사용한 경우

public void readFile(String filePath) throws IOException {
    Files.readAllLines(Paths.get(filePath));  // 파일 읽기
}

public void execute() throws IOException {
    readFile("test.txt");  // 예외를 던짐
}

📋 코드 흐름

  1. readFile() 메서드에서 **IOException**이 발생할 수 있음.
  2. 예외가 발생하면 호출된 메서드(execute())로 예외가 던져짐.
  3. execute()를 호출하는 메서드가 예외를 처리해야 함.

🔧 (2) try-catch를 사용한 경우

public void readFile(String filePath) {
    try {
        Files.readAllLines(Paths.get(filePath));  // 파일 읽기
    } catch (IOException e) {
        e.printStackTrace();  // 예외 처리
    }
}

📋 코드 흐름

  1. readFile() 메서드에서 **IOException**이 발생할 수 있음.
  2. 예외가 발생하면 catch 블록에서 직접 처리.
  3. 호출한 메서드는 예외를 처리할 필요가 없음.

📋 5. 언제 throws를 사용하고, 언제 try-catch를 사용해야 할까?

(1) throws를 사용하는 경우

  • 예외를 처리할 책임이 메서드를 호출하는 쪽에 있을 때.
  • 메서드 내부에서 예외를 처리할 방법이 없을 때.

📋 예시: 파일 읽기 메서드

public void readFile(String filePath) throws IOException {
    Files.readAllLines(Paths.get(filePath));
}

(2) try-catch를 사용하는 경우

  • 예외를 메서드 내부에서 바로 처리할 수 있을 때.
  • 예외 처리 로직이 간단하고 명확할 때.

📋 예시: 네트워크 요청 처리

public void fetchData(String url) {
    try {
        // 네트워크 요청
    } catch (IOException e) {
        e.printStackTrace();
        System.out.println("데이터 가져오기 실패");
    }
}

🧪 6. 실무에서의 예시: throws와 try-catch의 차이

상황 예외 처리 방식 이유
파일 읽기/쓰기 throws 사용 예외를 호출하는 쪽에서 처리해야 함
네트워크 요청 실패 try-catch 사용 실패 시 바로 에러 메시지 출력
데이터베이스 연결 throws 사용 예외를 호출한 쪽에서 처리해야 함
사용자 입력 검증 실패 try-catch 사용 입력 오류 시 바로 처리

💡 7. 공통점과 차이점 요약

공통점 차이점
둘 다 예외를 처리하기 위한 도구 throws는 예외를 던짐, try-catch는 예외를 처리
둘 다 예외 유형을 지정 가능 throws는 호출 스택을 따라 예외 전달, try-catch는 직접 처리
코드의 안정성을 높임 throws는 코드가 짧아짐, try-catch는 코드가 길어질 수 있음

🎯 8. 최종 결론

질문 답변
throws와 try-catch의 공통점은? 둘 다 예외(Exception)를 처리하거나 전달하기 위한 도구.
throws와 try-catch의 차이점은? throws는 예외를 호출하는 쪽에서 처리하도록 위임, try-catch는 직접 처리
throws로 예외가 발생한 위치를 알 수 있나요? 예외가 발생한 메서드와 호출 스택(trace)을 자동으로 출력합니다.

그런데, 예외를 처리한다는 건 구체적으로 어떤 의미이며 어떻게 처리한다는거야?
그리고 throws 는 예외를 던진다고 했는데
던진다는 건 구체적으로 어떤 의미이고 어떻게 던진다는거야?
훌륭한 질문이에요! 😊

개발을 하다 보면 "예외 처리", **"예외를 던진다(throws)"**는 표현을 많이 사용하지만, "처리한다"와 "던진다"는 정확히 어떤 의미인지 궁금할 수 있어요.

그래서 이걸 아주 구체적으로 설명해드릴게요!
"예외 처리"가 뭘 의미하는지, "던진다"는 표현이 어떤 동작을 하는 건지, 그리고 이 과정에서 어떤 일이 일어나는지를 단계별로 풀어드릴게요.


🎯 1. 예외(Exception)란 무엇인가?

예외(Exception)의 정의

  • **"예외"**란 프로그램이 예상하지 못한 문제가 발생한 상황을 말합니다.
  • 예외가 발생하면 프로그램의 정상적인 흐름이 중단될 수 있기 때문에, 예외를 처리해서 프로그램이 계속 실행될 수 있도록 해야 합니다.

🧩 예시: 파일 읽기 작업

public void readFile(String filePath) throws IOException {
    Files.readAllLines(Paths.get(filePath));
}

여기서:

  • 파일 경로가 잘못되었거나, 파일이 없는 경우 IOException 예외가 발생할 수 있어요.
  • 이때 예외를 처리하지 않으면 프로그램이 중단됩니다.

🔎 2. 예외를 처리한다는 건 무슨 의미일까?

예외를 처리한다는 것 = 예외 상황을 인지하고, 그에 맞는 대처를 한다는 의미

예외를 처리하는 방법은 여러 가지가 있습니다:

1️⃣ 문제의 원인을 로그에 남기고 프로그램을 종료하지 않고 계속 실행
2️⃣ 사용자에게 적절한 에러 메시지를 보여주고, 정상 흐름으로 돌아가도록 유도
3️⃣ 예외 상황을 복구할 수 있다면, 복구하는 코드 작성


📋 예시: 예외 처리가 없는 경우

public void readFile(String filePath) {
    Files.readAllLines(Paths.get(filePath));  // 파일이 없으면 예외 발생
}
  • 만약 파일이 없는 경우, IOException 예외가 발생하면서 프로그램이 중단됩니다.
  • 사용자에게는 무의미한 에러 메시지가 뜨고, 프로그램은 더 이상 실행되지 않습니다.

📋 예시: 예외 처리한 경우 (try-catch)

public void readFile(String filePath) {
    try {
        Files.readAllLines(Paths.get(filePath));
    } catch (IOException e) {
        System.out.println("파일을 찾을 수 없습니다. 경로를 확인하세요.");
    }
}
  • 예외가 발생하면 catch 블록에서 예외를 잡아냅니다.
  • 프로그램이 중단되지 않고, 사용자에게 적절한 메시지를 보여줄 수 있습니다.

🔧 3. 예외를 "던진다"는 건 무슨 의미일까? (throws)

"던진다"는 건 예외 처리를 메서드 호출한 쪽에 넘긴다는 의미

throws는 현재 메서드에서 발생할 수 있는 예외를 내가 처리하지 않고, 이 메서드를 호출한 쪽에서 처리하도록 위임하는 겁니다.


📋 예시: 예외를 던지는 코드

public void readFile(String filePath) throws IOException {
    Files.readAllLines(Paths.get(filePath));
}
  • 여기서 **throws IOException**은 이 메서드가 IOException 예외를 발생시킬 수 있음을 알리는 것입니다.
  • 이 메서드를 호출하는 쪽에서 예외를 처리해야 합니다.

📋 예외를 던지는 흐름 예시

  1. readFile() 메서드에서 예외 발생
  2. 예외가 처리되지 않고 호출한 메서드로 던져짐
  3. 호출한 메서드에서도 예외를 처리하지 않으면, 그 메서드를 호출한 메서드로 또 던져짐
  4. 최종적으로 main() 메서드까지 예외가 도달하면 프로그램이 종료됨

🔧 4. throws와 try-catch의 차이점 요약

구분 throws try-catch
의미 예외 처리를 호출한 쪽에 위임 예외를 직접 처리
예외 발생 시 호출 스택을 따라 예외가 전파됨 예외를 즉시 잡아내고 처리함
사용 목적 메서드가 발생 가능한 예외를 선언할 때 예외가 발생한 곳에서 바로 처리할 때

🧪 5. 예외 전파 흐름을 시뮬레이션 해보자

코드 예시: 예외 전파 흐름

public void level1() throws IOException {
    level2();
}

public void level2() throws IOException {
    level3();
}

public void level3() throws IOException {
    throw new IOException("예외 발생!");
}

📋 실행 흐름

1️⃣ level3() 메서드에서 예외 발생
→ IOException: 예외 발생! 예외가 던져짐.

2️⃣ level2()로 예외가 전파됨
→ level2() 메서드는 예외를 처리하지 않기 때문에 다시 level1()로 예외가 던져짐.

3️⃣ level1()로 예외가 전파됨
→ 최종적으로 예외가 main() 메서드로 전파되며 프로그램이 중단됩니다.


예외를 잡아내는 코드로 수정

public void level1() {
    try {
        level2();
    } catch (IOException e) {
        e.printStackTrace();
    }
}

이제 예외가 발생하더라도 프로그램이 중단되지 않고 스택 트레이스(에러 발생 위치)를 출력합니다.


🎯 6. 결론: throws와 try-catch의 차이점

질문 답변
예외를 "처리"한다는 건 무슨 의미인가? 프로그램이 중단되지 않도록 예외 상황을 인지하고 적절히 대응하는 것.
예외를 "던진다"는 건 무슨 의미인가? 예외 처리를 메서드를 호출한 쪽에 위임하는 것.
throws와 try-catch의 차이점은? **throws**는 예외를 전파하고, **try-catch**는 예외를 즉시 처리.