Spring

- 생성자 주입을 권장하는 이유

https://smirkdev.tistory.com/46

 

의존성 주입(DI) 시에 생성자 주입을 사용해야하는 이유

의존성 주입 방법들 - 생성자 주입 가장 많이 보게 되는 생성자 주입방식이다. 생성자를 통해 객체를 주입해주며 Spring 프레임워크 자체에서도 생성자주입을 권장하기 때문에 생성자가 하나만

smirkdev.tistory.com

 

 

- Filter와 OncePerRequestFilter의 차이

사용자의 요청을 받으면 서블릿이 생성되어 메모리에 저장되고, 같은 클라이언트의 요청을 받으면 이 서블릿 객체를 재활용한다.

근데 어떤 서블릿이 다른 서블릿으로 dispatch된다면 그 서블릿에 도착하기 전 다시 한번 FilterChain을 거치면서 원치 않게 필터를 두번 거치게 된다. 

OncePerRequestFilter는 사용자의 request에 딱 한번 실행되는 필터이고 doFilter가 아닌 doFilterInternal을 꼭 Override 해주어야 한다.

 

알고리즘

- 프로그래머스 : n^2 배열 자르기(Level 2, 구현)

 

느낀 점

 

'TIL' 카테고리의 다른 글

[23.12.19]  (1) 2023.12.19
[23.12.18]  (0) 2023.12.18
[23.12.14]  (0) 2023.12.15
[23.12.13]  (0) 2023.12.13
[23.12.12]  (0) 2023.12.12

의존성 주입 방법들

- 생성자 주입

 

가장 많이 보게 되는 생성자 주입방식이다.

생성자를 통해 객체를 주입해주며 Spring 프레임워크 자체에서도 생성자주입을 권장하기 때문에

생성자가 하나만 존재한다면 자동으로 주입을 해준다.

만약 생성자가 여러개인 경우에는 빈 등록에 쓰일 생성자에 @Autowired를 붙여주어야 한다.

 

- 수정자(Setter) 주입

 

다음은 수정자 setter를 만들어주어 주입을 해주는 방식이다.

변동가능성이 있기 때문에 객체에 final이 빠졌고 스프링 초기에는 자바 기본 스펙이 get, set 이었으므로 수정자 주입을 많이 사용했다고 한다.

객체를 변경할 일이 혹시라도 있으면 수정자 주입을 사용해야할 것이다.

 

- 필드 주입

 

마지막으로 그냥 알아서 때려박아달라는 필드 주입이다. 이것도 final 사용이 불가능하고 아예 추천하지를 않는단다.

필드 주입은 Spring이 알아서 해주기 때문에 외부에서 수정이 불가능므로 이는 테스트 코드에 부적합한 방식이다.

또한 프레임워크에 굉장히 의존적이므로 별루다.

 

- 생성자 주입의 장점

1. 객체의 불변성 확보

수정자 주입과 필드 주입은 일단 final 사용이 불가능하므로 단일 객체를 보장할 수 없고, 변경가능성이 존재한다.(수정자는 게다가 public...)

정말 필요한 경우가 아니라면 의존 관계가 변할 일은 거의 없으므로 생성자 주입을 통해 불변성을 보장하는 것이 유리하다.

 

2. 테스트 코드 작성의 편리함

필드 주입은 @Autowired가 필요하기 때문에 Spring 프레임워크에 의존적이다. (@Autowired없이는 아예 null이 뜬다.)

테스트 코드는 순수 자바로 작성하는 것이 좋으므로 생성자 주입이 유리하다.

 

3. 순환 참조 에러 방지

UserService가 PostService에 의존하고  PostService가 UserService에 의존하는 것을 순환 참조라고한다.

Bean으로 등록되기 위해 서로가 서로를 필요로 하는 Deadlock같은 상황이 벌어진다.

@Autowired는 모든 객체가 생성된 후에 의존관계 주입이 처리되기 때문에 어플리케이션이 일단 실행이 된다.

그 후 상대 객체를 통해 호출하는 메소드들이 계속 쌓이면서 stack overflow가 발생하게 된다.

그에 비해 생성자 주입은 객체의 생성과 의존관계 주입이 동시에 일어나기 때문에 compile 상황에서 미리 순환 참조를 확인할 수 있다.

 

Reference

https://velog.io/@mangtaeeee/Spring-%EC%83%9D%EC%84%B1%EC%9E%90-%EC%A3%BC%EC%9E%85%EC%9D%84-%EC%82%AC%EC%9A%A9%ED%95%B4%EC%95%BC-%ED%95%98%EB%8A%94-%EC%9D%B4%EC%9C%A0

 

https://mangkyu.tistory.com/125

 

'Spring' 카테고리의 다른 글

[Spring] DI(의존성 주입)는 무엇일까  (0) 2023.12.12

Spring

- 오류 해결

Validation @Pattern(regexp) 관련 오류

 

위와 같이 최소 길이가 3이고 영어 대소문자 + 숫자로 이루어진 닉네임을 받고자 @Pattern을 이용해 유효성 검증을 넣어주었다.

그 후 nickname에 "dongha1234"라는 값을 넣어주니

이렇게 유효성 검증에 실패했다는 메시지가 떴다.

 

해결방법은 정규식에 문자 길이 범위를 지정해주는 것이다.

여러가지 실험을 해보았는데 @Size 어노테이션은 min, max 모두 설정해도 정규식 관련 validation을 통과하지 못했다.

*를 사용할 것이 아니라면 길이 범위 지정이 필수적이다.

 

ResponseEnity와 Jackson 관련 에러

진행중인 프로젝트에서 RestControllerAdvice로 커스텀 예외들을 처리해주고 있는데

디버깅 결과 에러 던지는 곳까지는 잘 가지만 response를 제대로 보내주지 못하고 500이 뜨는 에러가 발생했다.

 

 

에러 로그는 잘 출력되지만 HttpMedia~ Exception이 발생하면서 전달이 안 된다.

 찾아보니 ResponseEntity의 body에 ErrorResponse가 담길 때 문제가 생긴 것이었다.

스프링이 선택한 Jackson이 ErrorResponse를 json 객체로 변환해서 ResponseEntity에 담아주어야 하는데.

Jackson에서 생성한 Objectmapper는 json으로의 변환을 getter 메소드를 통해 진행해준다. (setter는 안됨!!)

결론은 ErrorResponse 클래스에 @Getter 추가해주니 해결이 되었따.

알고리즘

- 프로그래머스 : 튜플(Level 2, 정렬)

느낀 점

 

'TIL' 카테고리의 다른 글

[23.12.18]  (0) 2023.12.18
[23.12.15]  (1) 2023.12.15
[23.12.13]  (0) 2023.12.13
[23.12.12]  (0) 2023.12.12
[23.12.11]  (0) 2023.12.12

Spring

- 정적 팩토리 메소드

생성자 대신에 public static  클래스 메소드를 통해 간접적으로 객체 생성을 유도하는 것을

정적 팩토리 메소드라고 부른다.

 

정적 팩토리 메소드의 장점

1. 생성 목적에 대한 이름 표현이 가능.  new Product() 이런 것 보다는

Product.productMadeOf, Product.productMadeFrom 등등 메소드의 이름을 통해 객체의 특성에 대해 묘사할 수 있다.

2. 인스턴스에 대해 관리가 가능하다.

메소드를 통해 간접적으로 객체를 생성하기 때문에 객체의 생성에 대한 통제가 가능하다.

객체를 싱글톤으로 쓰고 싶다면 private static 으로 필드에 객체를 하나 선언해 두고, getInstance()를 통해 새로 생성해주든(필드값이 null이라면)  저장된 객체를 받아오든 할 수 있다. (해시 맵으로도 인스턴스를 관리할 수 있다.

3. 인터페이스에 사용하면 하위 객체(구현체)를 받아올 수 있다.

4. 캡슐화, 정보 은닉

알고리즘

- 프로그래머스 : 메뉴 리뉴얼(Level 2, 완전탐색)

느낀 점

 

'TIL' 카테고리의 다른 글

[23.12.15]  (1) 2023.12.15
[23.12.14]  (0) 2023.12.15
[23.12.12]  (0) 2023.12.12
[23.12.11]  (0) 2023.12.12
[23.12.08]  (0) 2023.12.08

Spring

- JPA 연관관계를 끊고 진행한 프로젝트 회고

https://github.com/Four-Talking/Nateam

 

GitHub - Four-Talking/Nateam

Contribute to Four-Talking/Nateam development by creating an account on GitHub.

github.com

 

- Entity 클래스에 @NoArgsConstructor(access=AccessLevel.PROTECTED)를 사용하는 이유

일단 protected는 같은 패키지나 자식 클래스에서 사용할 수 있는 접근 제어자이다.

 

Entity가 지연 로딩에서 조회를 할 때 실제 엔티티가 아닌 프록시 객체를 조회한다.

JPA는 기본 생성자를 통해 프록시 객체를 생성하는데 private면 객체 생성이 불가한 것이다.

 

그러나 em.find거나 즉시 로딩처럼 실제 Entity를 가져오는 경우에는 private 여도 상관은 없다.

Post에 지연로딩을 설정해놓았다 가정하고(지연 로딩이기 때문에 post의 accesslevel은 무관), User에 private을 설정하면 post.getUser(); 을 했을 때 NoSuchMethodException이 터진다.

알고리즘

- 프로그래머스 : 디펜스 게임(Level 2, 우선순위 큐)

계속 정렬해가면서 최대, 최소의 수(혹은 몇개의 수)를 추적해야할 때는 우선순위큐를 먼저 떠올리자.

원소를 넣어줄때마다 벡터로 정렬했더니 시간초과가 났다.

느낀 점

 

 

'TIL' 카테고리의 다른 글

[23.12.14]  (0) 2023.12.15
[23.12.13]  (0) 2023.12.13
[23.12.11]  (0) 2023.12.12
[23.12.08]  (0) 2023.12.08
[23.12.06]  (0) 2023.12.06

Spring

DI(의존성 주입은)는 무엇일까?

알고리즘

- 프로그래머스 : 시소 짝꿍(Level 2, 구현)

 

느낀 점

'TIL' 카테고리의 다른 글

[23.12.13]  (0) 2023.12.13
[23.12.12]  (0) 2023.12.12
[23.12.08]  (0) 2023.12.08
[23.12.06]  (0) 2023.12.06
[23.12.05]  (1) 2023.12.06

Spring과 JPA를 활용해 처음 개발을 해 가면서, 문득 내가 CRUD를 작동하게는 하지만 왜 이런 코드를 작성했는가에 대해서는 모르는 것도 많고 알아도 얕게 알아서 조리있게 답변하지 못하는 내용들이 많은 것을 깨달았다.

Spring의 핵심 원리인 IOC라는 설계원칙, 이를 충족시키는 DI라는 디자인 패턴, 그리고 생성자를 통한 DI를 권장하는 이유에 대해서 정리해 보려 한다.

강한 결합

DI가 필요한 이유를 이해하기 위해선 먼저 강한 결합을 가지는 클래스의 관계에 대해 생각해 보아야 한다.

 

public class Customer {

	private Chicken chicken;
    
    public Customer() {
    	this.chicken = new Chicken();
    }
}

 

고객이라는 클래스는 치킨이라는 음식을 먹을 수 있다.

그러나 이러한 클래스설계는 두 클래스가 강하게 결합되어 있다는 단점을 가진다.

고객 클래스는 먹을 수 있는 메뉴가 치킨 뿐이다. 만약 다른 음식을 먹고 싶다면, Chicken이라는 필드값을 Pizza로 수정해야 하는 수고로움이 있다.

즉 Customer 클래스는 Chicken 클래스와 강하게 결합되어 있어 다른 음식으로 교체만 하고 싶음에도 Customer 클래스 자체를 수정해야하는 번거로움이 있다.

이는 Customer 클래스가 Chicken이라는 클래스에 '의존' 하고 있기 때문이다.(Customer가 Chicken을 사용하고 있는 것 = Customer가 Chicken에게 의존)

 

Dependency Injection(의존성 주입)

이제 의존성 주입을 통해서 강한 결합을 느슨하게 만들어보자

일단 위 클래스에서 가장 문제가 되는 것은 Customer가 다른 음식을 먹고 싶을 때마다 Customer 클래스의 필드까지 수정해주어야 하는 번거로움이 있다는 것이다.

이 자체는 객체지향의 특징 다형성을 통해서 해소가 가능하다.

 

먼저 Food라는 interface를 선언해주고 Chicken이 Food를 Implements 하게 한다면

public class Customer {

	private Food food;
    
    public Customer(Food food) {
    	this.food = food;
    }
}

 

이런 식의 클래스 형태가 만들어진다.

그러면 나중에 Customer의 생성자 인자에 원하는 Food를 갈아끼워주기만 하면 된다.

이러면 클래스를 수정할 필요가 없으므로 편의성과 다른 Food들에 대한 확장성, 유연성도 좋아진다.

 


//기존 Customer 사용 방법 Chicken 클래스에게만 의존이 가능

Customer customer = new Customer();

//수정 후 Customer 사용 방법

Food food = new Pizza();

Customer customer = new Customer(food);

 

이제 우리는 Food 인터페이스에 구체 클래스를 확정하고, Customer 클래스가 무엇을 의존할지 '의존성을 주입' 해주면서 강한 결합을 느슨하게 만들어 주었다.

여기서 나오는 것이 IOC의 개념이다.

DI를 하지 않은 설계에서는  

Customer -> Chicken 이라는 구체 클래스로 제어가 진행되었다면

DI를 한 설계에서는

Food를 확정한 후 -> Customer에 주입하는 식으로 제어의 역전이 일어난 것이다.

ex) DI를 통해 Repository -> Service -> Controller 로 제어의 역전.

 

즉 DI라는 디자인 패턴을 통해 IOC라는 설계 원칙을 충족한 것이다.

 

Spring에서는 DI 컨테이너를 제공하여 application 실행 시점에 Bean을 생성하고

특정 annotation이 붙은 클래스들을 탐색하며 의존성 주입들을 알아서 쫙쫙해준다!

DI 컨테이너가 Bean들의 생명주기와 객체 관계를 알아서 해주는 것도 IOC라고 볼 수 있다.

(우리가 직접 지정해주던 관계 -> JVM, 에서 Spring이 정해주는 관계와 Bean, 생명주기 -> 우리가 사용)

 

DI의 장점

강한 결합을 느슨하게 만들어줘 확장성과 유연성을 보장한다!!

Spring

이모저모

equals 메소드 사용할 때 앞에 확실한 변수나 자료형을 넣어주는 이유

-> a.eqauls(b) 할때 a가 null이라면 NPE가 뜬다.

 

변수명 바꿀 떄는 shift + f6으로 한번에 바꾸자(intellij)

 

boolean 자료형을 반환하는 메소드 이름은 예-아니오로 대답할 수 있는 문장으로 지어라.

 

소셜 로그인 유저와 회원가입한 유저를 어떻게 구분지을 것인가.

한 테이블에 가입한 네트워킹 서비스 정보를 저장하고, 각각 필요한 필드값이 다를 때는 nullable이나 기본값을 줘서 관리한다

vs

가입을 지원하는 네트워크 서비스마다 테이블을 따로 두어 관리한다.

 

전자의 장점은 일단 구현이 단순해져 작업시간 측면의 리소스 이득을 본다.

단점은 결합도가 매우 높아진다.

 

후자의 장점은 각 테이블에서 변동사항이 있을 때 서로 영향을 주지 않는다.(해당하는 테이블에 column만 추가하면 됨)

단점은 구현 난이도가 올라간다. 테이블이 생길수록 어떤 테이블을 참조해야하는 지 다 체크해줘야하고 어디까지 나눌지 어디까지 결합할 지 생각해주는 게 매우 어려웠다.

 

현업에서는 두 케이스 모두 사용한다고 해서 선택의 문제인 것 같다. 실력을 올리 데에는 후자가 더 좋겠지만..

 

 

알고리즘

- 프로그래머스 : 테이블 해시 함수(Level 2, 정렬)

  내가 문제를 푼 IDE(visual studio)와 프로그래머스 상의 IDE 의 코드가 똑같음에도 불구하고 결과값이 다른 오류가 발생했다. 

-> 내가 vector 조회 인덱스를 잘못 넣어서 size가 2인 벡터에서 index 2에 접근했다. 

-> VS는 v[2] 값을 0을 반환해줬고 프로그래머스는 10을 반환해줬다.(쓰레기값)

하필 4랑 14 이렇게 10차이 나서 고민하느라 고생했다.

느낀 점

'TIL' 카테고리의 다른 글

[23.12.12]  (0) 2023.12.12
[23.12.11]  (0) 2023.12.12
[23.12.06]  (0) 2023.12.06
[23.12.05]  (1) 2023.12.06
[23.12.01]  (0) 2023.12.04

+ Recent posts