본문 바로가기

우아한테크코스 7기 프리코스

7기 프리코스 1주차 회고(알고 있다고 생각했던 것들)

1주차 프리코스를 진행하며 아는 것과 모르는 것을 구분하는 법을 배울 수 있었다.

평소 알고 있다고 생각하고 사용했던 개념들에 대해 꼬리질문을 하니 대답할 수 없던 경우가 많았다.

 

과제를 진행함에 있어 내가 알고 있다고 생각했지만, 몰랐던 것들에 대해 정리를 해 보고자 한다.

 

 

 

 

1. 자바의 record 클래스


알고 있던 개념

1. record 는 단순히 값을 저장하는 용도의 클래스가 필요할 때 사용하는 클래스 타입이라는 점.

2. 내부 변수를 얻기 위해선(get), 변수명과 동일한 메서드로 접근하면 된다는 것(ex. record.value()).

 

 

더 조사하게 된 계기

1. 불변 객체를 만들기 위해 record 클래스를 만들고, 변수에 private final 을 붙였다. 하지만, 문법 오류가 발생했다. 

2. 테스트를 위해 setter 를 만들었지만, 값을 set 하는 데에도 문법 오류가 발생했다. 내부 변수가 final 이라 set 이 불가능하다고 했다. 

 

 

몰랐던 개념

1. record 는 변경 불가한(불변) 데이터를 저장하는 클래스를 쉽게 만들 수 있게 해 주는 클래스 타입이다. 

     →  내부 변수가 자동으로 private final 로 설정된다.

2. 클래스 코드를 간결하게 하기 위해 사용되고, 값을 담는 데에 최적화되어있다.

3. 생성자(모든 변수를 매개변수로 받는), toString(), equals(), hashCode() 메서드가 자동으로 생성된다.

     → 이 점이 데이터를 저장하는 클래스를 “쉽게” 만들 수 있게 한다는 점에 크게 기여한다고 생각된다.

 

 

 

2. 자바의 final 키워드와 방어적 복사


알고 있던 개념

1. final 로 선언하면 해당 객체는 절대로 변경될 수 없다.

 

 

더 조사하게 된 계기

1. final 키워드를 통해 내부 List 변수를 선언했는데, getter 로 접근한 Listadd() 로 값을 추가할 수 있던 상황을 접했다.

 

 

몰랐던 개념

1. final 은 변수의 "참조"를 변경할 수 없다는 의미였다.

 Collection 처럼 내부 상태를 변경할 수 있는 자료구조에 final 을 붙이면, 재할당만 불가능하고, add() 등의 메서드는 사용 가능하다 → 값이 변경된다.

 

2. Collection을 불변 객체로 만들려면 불변 컬렉션을 사용하거나 방어적 복사를 수행해야 한다.

 예를 들어, List.copyOf() 또는 Collections.unmodifiableList()를 사용하여 불변 리스트를 생성할 수 있다. 또는 새로운 컬렉션 객체를 생성하여 원본 데이터를 복사함으로써 외부 변경으로부터 보호할 수 있다.

 

 

방어적 복사를 수행하는 법

public class A {

	List<Integer> values;
    
    public A(List<Integer> values) {
    	this.values = List.copyOf(values); // A 내부에서도 값은 변경할 필요가 없는 경우.
    }
    
	public List<Integer> getValues(){
		return List.copyOf(values); // 불변 List 로 방어적 복사.
	}
}

 

 

 

 

3. 싱글톤 패턴을 사용하는 이유


알고 있던 개념

1. 싱글톤 패턴을 사용하면, 프로그램 전역에서 특정 클래스의 인스턴스가 한 개만 생성되게 할 수 있다.

2. 싱글톤의 구현하려면, 생성자를 private 으로 만든 뒤, 자신의 인스턴스를 자신이 관리하게 하면 된다.

 

 

더 조사하게 된 계기

1. 의존성 주입을 담당하는 클래스를 싱글톤으로 관리되면 되지 않을까? 하고 막연하게 생각했었다.

2. 하지만, 싱글톤으로 관리하는 데에 이점을 분석하는 과정에서 제대로 된 근거를 찾을 수 없었다.

 의존성 주입 클래스는, 내부에서 의존성을 매핑해주고, getter 를 통해 의존성이 주입된 클래스의 인스턴스를 반환하고 있었다.
public class 의존성주입 {
	
    private final 클래스1 instance1= new 클래스1();
    private final 의존성_클래스1 instance2 = new 의존성_클래스1(instance1);
    // ....
    
    public 의존성_클래스1 get의존성_클래스1() {
    	return instance2;
    }
}



 의존성 주입 클래스는 Application 에서 Controller 를 생성하는 데에 쓰이고 있었고, Controller 를 제외한 어떠한 클래스에서도 사용되지 않고 있었다.

3. 의존성 주입 클래스를 막연하게 싱글톤으로 만들자고 생각하던 것을 보고, 싱글톤에 대한 이해가 부족하다고 느꼈다.

 

 

몰랐던 개념

1. 싱글톤을 왜 써야 하는가?

싱글톤 패턴의 장점

1. 유일 인스턴스를 가질 수 있다.
- 싱글톤 패턴이 적용된 클래스의 인스턴스는 애플리케이션 전역에서 단 하나만 존재하도록 보장된다.
- 여러 곳에서 하나의 인스턴스를 공유하므로, 불필요한 객체의 생성을 막을 수 있고, 여러 개의 객체가 생성되면서 발생할 수 있는 데이터 불일치를 방지할 수 있다.

2. 메모리를 절약할  수 있다.
- 애플리케이션 전역에서 하나의 인스턴스만 생성되므로, 여러 인스턴스 생성으로 인한 불필요한 메모리 자원 사용을 막을 수 있다.

 

2. 정말 싱글톤을 쓰는 게 더 좋은 선택이었는가?

의존성 주입을 하는 클래스가 싱글톤이면 어떤 것이 좋을까?
- 애플리케이션 전역에서 항상 같은 인스턴스(의존성이 주입된)를 얻을 수 있다.
- 하지만,
의존성 주입 클래스는 Application 에서 Controller 를 생성하는 데에 쓰이고 있었고, Controller 를 제외한 어떠한 클래스에서도 사용되지 않고 있었다 → 싱글톤으로써의 이점이 없었다.

 

 

 

 

4. JUnit5 의 @BeforeAll 과 생성자 방식의 차이


알고 있던 개념

1. 함수에 @BeforeAll 을 사용하면, 테스트 클래스가 초기화될 때, 처음 수행된다.

2. 보통, 테스트에 필요한 값을 설정할 때 사용한다.

 

 

더 조사하게 된 계기

1. 테스트 클래스에서 필요한 클래스들의 의존성을 주입하는 과정에서, @BeforeAll 을 사용했다. 하지만, @BeforeAll 이 테스트 클래스가 초기화될 때, 처음 수행된다면, 생성자와 다른 것은 무엇인지 의문이 들었다.

 

 

몰랐던 개념

1. @BeforeAll생성자의 차이점

생성자는 테스트 클래스가 초기화될 때 수행된다.
@BeforeAll 도 마찬가지이다.

차이점은 뭘까?
- 생성자: 각 테스트 메서드가 실행될 때마다 새로운 인스턴스가 생성되므로, 생성자는 매 테스트마다 호출된다.
- @BeforeAll: 클래스의 모든 테스트 메서드가 실행되기 전에 한 번만 실행된다.

 

2. 테스트 클래스의 동작 원리

- 테스트 클래스는 각 테스트 메서드별로 독립적으로 테스트하기 위해, 각 테스트마다 새로 초기화된다.

 

 

확인을 위해 생성자와 @BeforeEach 를 각각 사용해 보았다.(더미 테스트 3개)

1. 생성자를 사용하는 경우

@DisplayName("BeforeAllTest 테스트")
public class BeforeAllTest {

    public BeforeAllTest() {
        System.out.println("초기화");
    }
// 아래에는 항상 참인 테스트 메서드 세 개가 있음.

"초기화" 가 세 번 출력되었다.

 

2. @BeforeAll 을 사용할 경우

@DisplayName("BeforeAllTest 테스트")
public class BeforeAllTest {

    @BeforeAll
    static void beforeAll() {
        System.out.println("초기화");
    }
// 아래에는 항상 참인 테스트 메서드 세 개가 있음.

"초기화" 가 한 번 출력되었다.