dev-course day7

2rlokr·2025년 3월 12일

dev-course

목록 보기
7/43
post-thumbnail

오늘 배운 내용

예외(Exceptions)와 오류(Error)

예외 (Exception)

프로그램 동작 도중 모종의 이유로 수행 중인 동작에 예상하지 못한 영향을 받는 것

  • 예외도 객체이다. (예외도 예외가 아니다 ㅎㅋ)
  • Exceptionclass이며,Throwable 라는 class를 상속했다.
public class Main {
	public static void main(String []args) {
    	throw new IllegalArgumentException("예외도 객체처리 합니다.");
    }
}
  • new로만 exception을 만들면 프로그램이 그저 메모리에 올릴 뿐 예외를 인식 못할 수 있다.
  • 그래서 throw로 예외 상황을 띄워줘야 한다.

  • NullPointerException : 컴파일은 되지만 실행 도중 예외 상황이 발생한다.
  • IOException : 해결방법을 마련하지 않으면 아예 컴파일이 되지 않는다.

체크드 예외 (Checked Exception)

자바에서 컴파일 시점에 검사가 이루어지며, 프로그램이 예외 발생 가능성을 명시적으로 처리하도록 강제한다. 반드시 예외 처리가 필요하며, 이를 무시하면 컴파일 오류가 발생한다.

  • Exception을 바로 상속받은 예외들은 체크드 예외이다.
  • try-catch 를 이용하여 예외 처리를 해주거나, throws 키워드를 사용하여 예외처리를 호출하는 시점으로 미루는 방법이 있다.

언체크드 예외 (Unchecked Exception)

프로그램 실행 중에 발생하는 예외 중 컴파일러가 체크하지 않는 예외이다. 명시적으로 예외 처리를 하지 않아도 프로그램이 컴파일된다. 즉, 프로그램 실행 중에 예외가 발생할 수 있으며, 이를 사전에 처리하지 않으면 프로그램이 예기치 않게 종료될 수 있다.

  • RuntimeException 클래스를 상속받은 하위클래스들이 언체크드 예외이다.
  • NullPointerException도 이에 해당한다.

throws

  • 메서드에서 예외가 발생할 수 있음을 알리는 데 사용하는 키워드
  • 메서드 시그니처에서 사용한다.
  • 메서드가 호출될 때 특정 예외가 발생할 가능성이 있다는 것을 메서드를 사용하는 시점에 알린다.
  • Checked Exception 일 때
  1. throws가 있으면, 해당 메서드를 호출하지 않는다면 컴파일 에러가 발생하지 않는다. (예외처리를 호출 시점에 미루는 것)
  2. throws가 없으면 컴파일 중 오류가 발생한다.
  • 언체크드 예외일 때는, 메서드를 실행한다.

예외의 처리

프로그램 실행 중 발생할 수 있는 예외를 적절하게 관리하여 프로그램이 중단되지 않고 안정적으로 실행될 수 있도록 하는 방법

try-catch-finally

try {
	// 예외 발생 가능성이 있는 명령문
} catch (예외타입 변수명) {
	// 예외 발생 시 처리할 명령문
} finally {
	// **예외 처리와 상관없이 반드시 실행**되는 명령문
    // finally는 생략 가능
}
  • try-catch 에서 예외가 1개 이상의 catch문에 걸릴 수 있을 때, 가장 먼저 걸리는 catch문에 들어가게 된다.

예외 발생

  • 원하는 시점에 예외를 강제로 발생시키는 것
  • 예외 역시 객체이기 때문에 throw를 사용하여 예외를 발생시킬 수 있다.
try {
	throw new Exception("예외");
} catch(Exception e) {

오류 (Error)

시스템 수준에서 발생하는 심각한 문제를 나타내며, 일반적으로 프로그램에서 처리할 수 없는 상황을 의미

  • ErrorThrowable을 상속했기 때문에 try-catch를 구현할 수 있지만, 이를 통해 처리하는 걸 절대 추천하지 않는다. => 예외적으로 시스템에 의해 자동으로 처리되거나, 발생 즉시 프로그램을 중단시킬 수 있다.

오류 vs 예외

  • 예외는 프로그래밍 오류 또는 예상 가능한 상황에서 발생하며, try-catch 블록을 사용해 적절히 처리할 수 있다.
  • 오류는 시스템의 심각한 문제를 나타내며, 이는 JVM이 자체적으로 처리해야 하거나, 프로그램이 더 이상 정상적으로 실행될 수 없기 때문에 프로그램을 종료하게 된다.

제네릭 (Generic)

클래스나 메서드를 정의할 때, 데이터 타입을 일반화(Generalization)하여 사용할 수 있게 하는 기능

제네릭 클래스

클래스 정의 시 타입 매개변수를 사용하여 다양한 데이터 타입을 처리할 수 있는 클래스

public class 클래스명<문자> { // 제네릭 클래스에 쓰인 <문자>는 메서드에 쓰인 제네릭과 별개다.
	public 문자 field1;
    
    public 문자 method1() {
    	...
    }
}
  • MyGeneric<String, int, boolean> generic = new MyGeneric<>(); 와 같이 primitive type은 타입으로 왜 못 넣을까?
    - 제네릭은 타입이 정해져있지 않기 때문에 NULL을 포함할 수 있어야 한다.
    • 그러므로, 기본 자료형이 아니라 참조 자료형을 사용해야 한다.
  • 제네릭 타입은 한 번 지정되면 인스턴스가 없어질 때까지 타입이 유지된다.

타입 매개변수

클래스, 인터페이스, 메서드를 정의할 때 사용하는 형식화된 변수이다. 이 변수는 구체적인 데이터 타입(자료형)이 아닌 타입 그 자체를 나타낸다. (이 말은 N이라고 해서 무조건 Number만 가능한 게 아니라는 뜻) 해당 타입은 제너릭 클래스나 메서드를 사용하는 시점에 지정된다.

타입(Type)구분
<T>Type
<E>Element
<K>Key
<V>Value
<N>Number
public class Clazz<T> {
	public static T field1;
}

다이아몬드 연산자, <>

제네릭 타입의 객체를 생성할 때 타입을 명시하지 않고 간결하게 코드를 작성할 수 있도록 도와주는 연산자

Box**<String>** myBox = new Box<String>();

  • Box의 타입 파라미터로 String을 명시하고 있다. 따라서 생성자에서 굳이 String을 기재하지 않아도 컴파일러가 충분히 해당 타입이 String인 것을 유추해 낼 수 있다.

Box<> myBox = new Box<String>(); : 문제 발생

Box<String> myBox = new Box<>();

  • 다이아몬드 연산자를 사용하면, 우측 생성자 부분에서 타입 파라미터를 생략할 수 있다.
  • 컴파일러가 변수 타입을 이미 알고 있기 때문에 생성자에 있는 타입은 생략할 수 있다.
Map<String, List<Integer>> map = new HashMap<String, List<Integer>>();

Map<String, List<Integer>> map = new HashMap<>();
  • 따라서 위와 같은 코드도 가능하게 된다.
  • 컴파일러가 좌변의 타입을 자동으로 유추하여 우변에 적용할 수 있다.

제너릭 메서드

접근제어자 static <제네릭 타입> 함수반환타입 메서드이름(<제네릭 타입> 매개변수, ...) {
}

// <T> 는 반환타입이 아니라 매개변수 타입 !! 
public <T> void printAry(T[] arr) {
	for (T el : arr) {
    	System.out.println(el);
    }
}

static with Generic

public class MyClass1<T> {
	
    public static void method1(T t) {
    	.. 	// 컴파일 에러 발생
	}
}

public class MyClass2 {
	pbulic static void <T> method2(T t) {
    	...
    }
}
  • Generic의 타입은 instance가 생성될 때 정해진다.
  • 컴파일 때, Generic 타입을 지정해준다.
  • MyClass1의 제네릭 타입 T는 인스턴스가 생성될 때 결정이 되는데, static 메소드는 인스턴스 생성과 별도로 메모리에 올라와 있다. 그렇다면 MyClass1이 생성되는 시점에 결정되는 제네릭 타입을 메서드가 매개변수로 받아낼 방법이 없다.
  • 반면, MyClass2의 메소드는, 호출되는 시점에 타입이 결정되기 때문에 컴파일 에러가 발생하지 않는다.

변수

  • 타입이 확정되지 않았기 때문에 메모리에 올릴 수가 없다.
  • 정적 영역에서는 generic을 사용할 수 없다.
public class MyStaticGeneric<T> {
	private static T name; // 불가능하다 -> private T name; 은 가능

메소드

  • static 제네릭 함수의 타입은 호출 시점에 확정된다. 즉, 제네릭 메서드의 타입 파라미터는 메서드가 호출될 때 구체화된다.
public class MyStaticGeneric<T> {
	public static <T> String test1(T data) { // String은 반환형, T는 매개변수 타입
		System.out.println("data = " + data);
		return "ok";
    }
}
  • static 메소드이기 때문에 직접 호출이 가능하다.
  • MyStaticGeneric.test1("Hello"); 와 같이 static인 함수를 인스턴스 생성없이 할 수 있다.

제네릭 타입제한 (Limiting Generic Type)

  • 타입 매개변수에 대해 특정 타입이나 타입 계층을 설정하여 제네릭 클래스나 메서드가 처리할 수 있는 타입을 제한하는 기능
  • extends 키워드를 사용하여 제네릭 타입 매개변수의 범위를 제한할 수 있다.

단일 클래스 타입 제한

public class Box<T extends Number> {
	public T field1;
    public T method1() {
    }
}
  • T는 Number Type의 하위 클래스 또는 그 자체여야 한다.

다중 클래스 타입 제한

interface A {
}

class B implements A{ // 가능
}

class C extends B{ // 가능
}

class D {
}

class GenericTest<T extends B & A> { // B의 하위 클래스 + A의 구현체가 올 수 있다.
} 

//        new GenericTest<D>(); 불가능
GenericTest<C> cGenericTest = new GenericTest<>();
GenericTest<B> bGenericTest = new GenericTest<>();
  • GenericTest의 타입 TB 혹은 A의 하위타입이어야만 한다.
  • Java에서는 다중 클래스를 제한할 수는 없으며, 클래스와 인터페이스를 조합하여 사용할 수 있다.

와일드 카드, ?

제네릭 타입 매개변수의 구체적인 타입을 알 수 없거나, 여러 가지 타입 중 하나를 허용할 필요가 있을 때 사용된다. => 어떠한 타입도 다 된다.

원래도 Generic이 다 되는 건데 와일드카드랑 같은 거 아닌가?

interface A {
}

class B implements A{
}

class C extends B{
}

class D extends C {
}

class E extends D {
}

//1. 
public void method1(Clazz<? extends C> a) {
// A,B : 불허, C,D,E : 허용
}

//2. 
public void method1(Clazz<? super C> a) {
// A,B,C : 허용, D,E : 불허
}
  1. 상한 제한 와일드 카드
  • C보다 상위 클래스는 올 수 없다. (C는 가능)
  1. 하한 제한 = 일반 Generic 이랑 동일하다.
  • C보다 상위 타입만 올 수 있다. C를 상속받은 건 올 수 없다. (C 가능)
  1. 무제한 와일드카드

Optional

Java에서 NullPointerException을 방지해주는, 즉, null인 값을 참조해도 NullPointerException이 발생하지 않도록 값을 래퍼로 감싸주는 타입

Optional 사용방법

Optional 생성 Static 메소드

1. empty()

.empty()

정보
기능 : 비어있는 Optional 객체를 생성
리턴값 : Optional<T>

예제 코드

public static <T> Optional<T> empty() {
	return new Optional<>(null);
}

Optional<Object> empty = Optional.empty();

2. of()

.of(T value)

정보
기능 : 전달된 값으로 새로운 Optional 객체를 생성 (Null일 수 없는, Null이라면 예외처리를 한)
리턴값 : Optional<T>

예제 코드

public static <T> Optional<T> of(T value) {
//        if(value == null) {
//            throw new NullPointerException();
//        } -> requireNonNull 함수랑 동일
        return new Optional<>(Objects.requireNonNull(value));
}

try{
	Optional.of(null);
} catch (NullPointerException e) {
	System.out.println("Null은 보관할 수 없습니다!");
    System.out.println(e.getMessage());
}

3. ofNullable()

.ofNullable(T value)

정보
기능 : 비어있을 수도 있고, 아닐 수도 있는 Optional 객체를 생성
리턴값 : Optional<T>

예제 코드

public static <T> Optional<T> ofNullable(T value) {
	return value == null
			? new Optional<>(null)
            : new Optional<>(value);
}

String imNull = null;
Optional<Object> opt1 = Optional.ofNullable(imNull);
Optional<Object> opt2 = Optional.ofNullable("Hello");

4. get()

.get()

정보
기본 : Optional 객체의 값을 가져온다.
리턴값 : T

예제 코드

public T get() { // 반환형 T
	if (data == null) {
		throw new NoSuchElementException("No value present");
	}
	return data;
}

Optional<Object> empty = Optional.empty();

try{
	empty.get();
} catch (NoSuchElementException e) {
	System.out.println(e.getMessage());
}

5. isEmpty()

.isEmpty()

정보
기능 : Optional 객체가 비어있는지 확인
리턴값 : boolean

예제 코드

public boolean isEmpty() {
	return data == null;
}

boolean isEmpty = empty.isEmpty();
}

6. isPresent()

.isPresent()

정보
기능 : Optional 객체가 있는지 확인
리턴값 : boolean

예제 코드

public boolean isPresent() {
	return data != null;
}

boolean isPresent = empty.isPresent();
}

오늘 궁금했던 것

Q. throws는 그럼 다른 개발자들에게 예외가 발생할 수 있다는 걸 알려주는 것이 주 목적인 건가요

A. 정확히는 해당 메서드를 사용하는 “주체”에게 해당 메서드는 예외가 발생할수 있다는 것을 알려주기 위해 사용합니다.
우리가 만드는 메서드는 좁게 바라보면 개발자가 사용하지만 시선을 넓게 바라보면 다른 시스템이 호출할 수도 있습니다. 따라서, 다른 개발자들에게 라기보다는 이 메서드를 호출하는 주체에게 예외가 발생할 수 있다는것을 알리는 용도로 사용한다로 보는게 더 좋을 것 같습니다 🙂

throws의 의미가 강의들을 때는 모호했는데 강사님이 대답해주신 답과 복습하면서 다시 보니 개념이 더 확실히 이해가 되었다.

오늘 있었던 문제점

아니 매번 실습 코드를 바로 타이핑하고 커밋하고 있었는데,,, 오늘 그냥 어쩌다 깃헙 profile overview에 들어갔는데.. 잔디밭에 커밋 기록이 없다..? 이게 뭔.. 하.. 찾아보니까 로컬에서 커밋할 때 email을 설정 안 해준 것이었다. 예전에 이미 다 해놨었는데 언제 풀린 건지.. 토큰 바꿀 때 이상하게 만졌나? 암튼 그래서 이전 커밋 기록이 다 없다..

해결 방법

우선 중요한 email 설정은 해주었다. 그래서 오늘 실습 커밋들은 다 정상적으로 올라가는 것을 확인하였다. 하지만, 이전 커밋 기록들은 여전히 기록되지 않았다. 해결 방법이 적힌 사이트를 참고해서 해보겠지만.. 너무 복잡해보인다. 그래서 걱정은 되지만 일단 해보겠다 ! :) 꼬이면 깃헙 언제나처럼 미워할 것이야 그러니까 (깃헙 너) 잘하도록.

참고 사이트

https://wellbell.tistory.com/43

팀 활동 후기

오늘도 2시간 팀원들과 열공했다. (물론, 따로하지만 혼자하는 것보다 좋은 듯..(?)) 오늘 RBF 어떻게 할지 다시 살짝 의논하고, 슬랙 채널도 활성화...하자고 하루에 적어도 메세지 하나는 남기기로 했다 ! 그리고 오늘 팀원 분 중 한 분이 ZEP이라는 플랫폼으로 혼자 공부할 때 플랫폼에서 같이 해보자! 라고 제안해주셔서 너무 좋았다 !!

느낀 점

지금까지 정말 많은 걸 배운 것 같다. 이제 갈수록 스프링에서 실제 잘 쓰는 기능(?)들이 함께 소개가 되는 것 같다. 스프링을 아직 잘 몰라서 겁이 난다 호호 그치만 뭐 잘 해내겠지 ~ 암튼 그래서 더 이해를 잘 하고 넘어가는 것이 중요할 것 같다 ! 오늘도 수고하였다 ! 벌써 수요일 금요일 언제 오나~..

0개의 댓글