본문으로 바로가기
제품데스크톱 도구TASTECAD도면 확인TASTEFILES파일 관리TASTEFTP파일 전송TASTESSH원격 작업공간TASTEZIP압축과 해제개발 도구TASTECODERust 통합 개발 환경TASTESTUDIO통합 개발·테스트 환경TASTEETL데이터 통합(ETL)서버TASTEREMOTE원격 데스크톱 서버TASTEGEO공간정보 서버TASTEMAIL메일 작업공간시작하기다운로드문서구매모든 제품
마켓플레이스
소식
개발소식오늘의 개발 소식개발 문서프로그래밍 언어RustTypeScriptJavaScriptPythonGoC#JavaCC++KotlinDart프런트엔드 프레임워크Next.jsReactVue.js · NuxtSvelteKitAngular서버 프레임워크Spring BootDjangoFastAPIASP.NET CoreNestJS · Express앱 · 데스크톱 프레임워크TauriFlutterReact NativeElectron알고리즘알고리즘 기초알고리즘 핵심알고리즘 심화실무 알고리즘
커뮤니티프로젝트개발 로그프롬프트AI 도구 이슈쇼케이스커뮤니티 안내
협업기술 전수홈페이지 구축데스크톱 앱 구축릴리스·결제 파이프라인판매 파트너문의
서비스원격 데스크톱설치 패키지 커스텀
TASTEDEV
제품

데스크톱 도구

TASTECAD복잡한 도면을 가볍게 열다TASTEFILES필요한 파일만, 가장 빠르게TASTEFTP파일이 오가는 가장 단정한 길TASTESSH서버와 나 사이, 더 단단한 연결TASTEZIP더 빠르게 묶고, 더 안전하게 풀다

개발 도구

TASTECODERust 앱을 편집 · 빌드 · 디버깅 · 테스트까지 한 창에서TASTESTUDIO편집부터 원격 테스트와 승인 기반 AI 수정까지TASTEETL값을 보며 데이터를 옮깁니다

서버

TASTEREMOTE우리 회사 서버로 운영하는 원격 데스크톱TASTEGEOGeoServer 호환 공간정보 서버를 Rust로 다시 세웁니다TASTEMAIL메일의 주도권을 다시 내 손에

시작하기

다운로드모든 제품 최신 판문서제품 사용법구매구독 · 영구 라이선스모든 제품모든 제품 한눈에
모든 제품
마켓플레이스소식
개발

소식

오늘의 개발 소식언어 · 프레임워크 · GitHub · AI 공식 발표개발 문서언어 · 프레임워크 · 알고리즘 안내서

프로그래밍 언어

RustTypeScriptJavaScriptPythonGoC#JavaCC++KotlinDart

프레임워크

프런트엔드 프레임워크Next.jsReactVue.js · NuxtSvelteKitAngular
서버 프레임워크Spring BootDjangoFastAPIASP.NET CoreNestJS · Express
앱 · 데스크톱 프레임워크TauriFlutterReact NativeElectron

알고리즘

알고리즘 기초복잡도 분석 · 정렬 · 탐색 · 기본 자료구조 · 트리와 힙알고리즘 핵심그래프 탐색 · 최단 경로와 최소 신장 트리 · 동적 계획법 · 탐욕 알고리즘 · 분할 정복 · 백트래킹알고리즘 심화문자열 알고리즘 · 수학과 정수론 · 비트 연산 · 구간 트리 · 유니온 파인드실무 알고리즘코딩 면접 풀이 전략 · LRU 캐시 · 레이트 리미터 · 일관된 해시
개발 문서 전체 보기
커뮤니티
프로젝트빌더와 작업 발견개발 로그결정과 시행착오프롬프트재사용 가능한 지시AI 도구 이슈Claude · Codex · Gemini 문제와 우회법쇼케이스완성된 결과커뮤니티 안내공개하고 함께 만드는 방식
협업
기술 전수Rust·egui 네이티브 앱 만들기홈페이지 구축10개 언어, 계정, 문서·다운로드데스크톱 앱 구축Rust·egui, WebView 없이릴리스·결제 파이프라인서명 · MSI·deb·rpm·AppImage · 해시판매 파트너소개하고 결제액의 30% 받기문의구축 · 제휴 · 투자 문의
서비스
원격 데스크톱브라우저로 내 컴퓨터에 접속설치 패키지 커스텀필요한 기능만, 설정은 미리
구매
KO
한국어EnglishDeutschEspañolFrançaisItalianoPortuguês (Brasil)日本語简体中文繁體中文
로그인
로그인
TASTEDEV

작은 도구부터, 더 좋은 개발까지.

support@tastedev.netTASTEDEV 후원하기
제품제품군구매문서다운로드마켓플레이스고객 지원
커뮤니티프로젝트개발 로그프롬프트AI 도구 이슈쇼케이스
정책개인정보처리방침이용약관구독 약관환불 정책커뮤니티 운영 정책
계정로그인가입하기
방문자
오늘
012345678901234567890123456789012345678901234567890123456789
어제
012345678901234567890123456789012345678901234567890123456789
누적 조회
0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789
상호
지엑스소프트
대표자
최영구
사업자등록번호
853-44-00368
주소
인천광역시 미추홀구 경인로 351 9층 V24
전화
+82-10-4404-1523
이메일
whitecalvin@tastedev.net
개인정보 보호책임자
최영구

© 2026 TASTEDEV제품 정보는 현재 검증된 개발 소스를 기준으로 합니다.출시·고도화 중

Java 문서
Java
시작하기기본 문법타입과 데이터모듈과 패키지오류 처리와 테스트핵심 개념
토론

Java 안내서 · 5/6

오류 처리와 테스트

Java는 오류를 예외(exception)로 알립니다. 예외는 두 종류로 나뉘는데, 컴파일러가 처리를 강제하는 검사 예외(checked)와 강제하지 않는 비검사 예외(unchecked)입니다. 이 장에서는 두 예외의 차이, try-catch-finally 와 try-with-resources, 직접 예외를 정의하는 방법, 그리고 JUnit으로 코드를 테스트하는 방법을 다룹니다.

검사 예외와 비검사 예외

모든 예외는 Throwable 을 상속합니다. 그 아래 Exception 계열은 프로그램이 다룰 수 있는 문제이고, Error 계열(OutOfMemoryError 등)은 보통 잡지 않는 심각한 문제입니다.

구분예컴파일러 강제
검사 예외IOException, SQLException잡거나 throws 로 선언해야 함
비검사 예외NullPointerException, IllegalArgumentException강제하지 않음
오류OutOfMemoryError, StackOverflowError보통 처리하지 않음

비검사 예외는 RuntimeException 을 상속합니다. 대체로 잘못된 인자나 상태처럼 프로그래밍 실수를 뜻하고, 검사 예외는 파일이나 네트워크처럼 호출자가 대비해야 하는 외부 실패를 뜻합니다.

package com.example.config;

import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;

public class ConfigLoader {
    // 검사 예외는 throws 로 호출자에게 넘길 수 있다
    static String load(Path path) throws IOException {
        return Files.readString(path);
    }

    static int parsePort(String value) {
        int port = Integer.parseInt(value); // NumberFormatException(비검사)
        if (port < 1 || port > 65535) {
            throw new IllegalArgumentException("port out of range: " + port);
        }
        return port;
    }
}

try-catch-finally

예외를 잡으려면 try 블록으로 감싸고 catch 에서 처리합니다. 처리 방식이 같은 예외는 | 로 한 번에 잡을 수 있습니다(서로 상속 관계인 예외는 함께 적을 수 없습니다). catch 는 위에서부터 차례로 검사하므로 구체적인 예외를 먼저 적습니다. finally 블록은 예외가 나든 안 나든 항상 실행됩니다.

try {
    String text = ConfigLoader.load(Path.of("app.conf"));
    int port = ConfigLoader.parsePort(text.strip());
    System.out.println("port = " + port);
} catch (NoSuchFileException | AccessDeniedException e) {
    System.err.println("설정 파일에 접근할 수 없습니다: " + e.getFile());
} catch (IOException e) {
    System.err.println("설정 파일을 읽지 못했습니다: " + e.getMessage());
} catch (IllegalArgumentException e) { // NumberFormatException 포함
    System.err.println("잘못된 포트 값: " + e.getMessage());
} finally {
    System.out.println("설정 읽기 시도 끝");
}

예외를 잡아서 아무것도 하지 않고 넘기면 문제가 숨어 버립니다. 그 자리에서 처리할 수 없다면 잡지 말고 위로 던지거나, 의미 있는 예외로 감싸서 다시 던지는 편이 좋습니다.

try-with-resources

파일, 소켓, 데이터베이스 연결처럼 사용 후 닫아야 하는 자원은 try-with-resources로 다룹니다. 괄호 안에서 연 자원은 블록이 끝날 때 예외가 나더라도 자동으로 close() 됩니다. AutoCloseable 을 구현한 객체라면 무엇이든 쓸 수 있고, 여러 자원을 세미콜론으로 나열하면 연 순서의 반대로 닫힙니다.

static long countErrors(Path log) throws IOException {
    try (BufferedReader reader = Files.newBufferedReader(log)) {
        return reader.lines()
                .filter(line -> line.contains("ERROR"))
                .count();
    } // reader.close()가 자동으로 호출됨
}

사용자 정의 예외

도메인에 맞는 예외를 만들면 호출자가 실패 원인을 구분하기 쉬워집니다. 호출자가 반드시 대비해야 하면 Exception 을, 그렇지 않으면 RuntimeException 을 상속합니다. 원래 예외를 cause 로 넘기면 스택 추적에 원인이 함께 남습니다.

public class OrderNotFoundException extends RuntimeException {
    public OrderNotFoundException(long orderId) {
        super("order not found: " + orderId);
    }

    public OrderNotFoundException(long orderId, Throwable cause) {
        super("order not found: " + orderId, cause);
    }
}

JUnit으로 테스트하기

JUnit은 Java의 표준 테스트 프레임워크입니다. JUnit 5부터 쓰는 Jupiter API로 테스트를 작성하며, Maven에서는 org.junit.jupiter:junit-jupiter 를 test 범위 의존성으로 추가합니다(Gradle 설정은 앞 장 참고). 테스트 클래스는 src/test/java 의 같은 패키지에 둡니다.

<dependency>
  <groupId>org.junit.jupiter</groupId>
  <artifactId>junit-jupiter</artifactId>
  <version>${junit.version}</version>
  <scope>test</scope>
</dependency>

테스트 메서드에는 @Test 를 붙이고, Assertions 의 메서드로 결과를 확인합니다. assertThrows 는 예외가 발생하는지 검사하고 그 예외를 돌려주므로 메시지까지 확인할 수 있습니다.

package com.example.config;

import static org.junit.jupiter.api.Assertions.*;

import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.ValueSource;

class ConfigLoaderTest {
    @Test
    @DisplayName("올바른 포트는 숫자로 바뀐다")
    void parsesValidPort() {
        assertEquals(8080, ConfigLoader.parsePort("8080"));
    }

    @Test
    void rejectsOutOfRangePort() {
        var e = assertThrows(IllegalArgumentException.class,
                () -> ConfigLoader.parsePort("70000"));
        assertTrue(e.getMessage().contains("70000"));
    }

    @ParameterizedTest
    @ValueSource(strings = {"0", "-1", "65536"})
    void rejectsInvalidPorts(String value) {
        assertThrows(IllegalArgumentException.class, () -> ConfigLoader.parsePort(value));
    }
}

mvn test 나 ./gradlew test 로 실행하면 실패한 테스트와 원인이 보고됩니다. @ParameterizedTest 는 같은 검사를 여러 입력으로 반복할 때 유용합니다. IDE에서는 테스트 메서드 옆의 실행 버튼으로 하나씩 실행하고 디버깅할 수 있습니다.

정리

  • 검사 예외는 처리나 throws 선언이 강제되고, 비검사 예외는 RuntimeException 을 상속합니다.
  • 닫아야 하는 자원은 try-with-resources로 열어 자동으로 정리합니다.
  • 도메인 예외를 만들 때는 원인(cause)을 함께 넘겨 추적 정보를 보존합니다.
  • JUnit Jupiter의 @Test, assertEquals, assertThrows 로 동작을 검증하고 빌드 도구로 실행합니다.
이 장에 대해 질문이 있나요?
이전 장모듈과 패키지다음 장핵심 개념

댓글 0개

인기 댓글순최신순

로그인 · 로그인하면 댓글을 남길 수 있습니다.