1. 클래스와 인스턴스의 차이에 대해 설명해주실 수 있을까요?

클래스(Class)와 인스턴스(Instance)는 객체 지향 프로그래밍(OOP, Object-Oriented Programming)에서 중요한 개념입니다.

클래스(Class) : 

클래스는 객체를 생성하기 위한 템플릿 또는 설계도라고 생각할 수 있습니다. 클래스는 데이터를 담을 수 있는 속성(필드 또는 프로퍼티)과 기능을 담을 수 있는 메서드로 구성됩니다. 예를 들어, "자동차"라는 클래스를 만들 경우 "색상", "제조사", "모델명" 등의 속성과 "운전하기", "멈추기" 등의 메서드를 정의할 수 있습니다.

인스턴스(Instance) : 

인스턴스는 클래스를 기반으로 실제로 생성된 객체를 의미합니다. 클래스는 설계도에 불과하므로, 실제로 작업을 수행하려면 이 클래스의 인스턴스를 생성해야 합니다. 예를 들어, "자동차" 클래스의 인스턴스를 생성하면, 각각의 인스턴스는 고유의 속성 값을 가지게 됩니다. 즉, "자동차1" 인스턴스는 "빨간색", "BMW", "X5" 등의 값을 가질 수 있고, "자동차2" 인스턴스는 "푸른색", "현대", "소나타" 등의 다른 값을 가질 수 있습니다.

따라서, 클래스는 객체의 공통된 특징을 정의하는 것이고, 인스턴스는  클래스에 따라 생성된 실체로 각각 고유의 속성을 가지게 됩니다.  둘의 관계는 "설계도와 그것을 토대로 만들어진 제품" 같은 관계라고   있습니다.

 

클래스란 객체를 만들어 내기 위한 설계도라고 볼 수 있다. 자세하게는 연관 되어 있는 변수와 메서드의 집합이라고 할 수 있다. 인스턴스란 설계도를 바탕으로 스프트웨어 세계에 구현된 구체적인 실체라고 볼 수 있다. 실체화 된 인스턴스는 메모리에 할당된다.

 

2. 클래스와 인스턴스는 객체 지향 프로그래밍 에 어떻게 적용되나요?

클래스와 인스턴스는 객체 지향 프로그래밍에서 중요한 개념입니다. 객체 지향 프로그래밍은 현실 세계의 개념과 동작을 소프트웨어 모델로 표현하는 방법을 제공합니다. 이를 위해 클래스와 인스턴스라는 개념을 사용합니다.

  • 클래스(Class): 클래스는 객체를 생성하기 위한 설계도 혹은 템플릿입니다. 클래스는 객체의 속성(멤버 변수)과 동작(메서드)을 정의합니다. 클래스는 객체를 생성하기 위한 공통된 특성과 동작을 모아놓은 것으로, 객체의 상태와 동작을 정의하는 추상적인 개념입니다. 클래스는 객체를 생성하기 위한 기반이며, 여러 개의 인스턴스를 생성할 수 있습니다.
  • 인스턴스(Instance): 인스턴스는 클래스를 기반으로 실제로 생성된 객체를 의미합니다. 클래스에서 정의한 속성과 동작을 가지며, 클래스의 인스턴스는 각각 독립적인 메모리 공간을 가지고 있습니다. 인스턴스는 클래스의 구체화된 개체로서, 실제로 프로그램에서 사용되는 데이터입니다. 클래스의 인스턴스는 클래스의 특성을 가지면서도 각각 다른 상태를 가질 수 있습니다.

클래스는 객체를 생성하기 위한 템플릿으로, 객체의 공통된 특성과 동작을 정의합니다. 이렇게 정의된 클래스는 프로그램에서 여러 개의 인스턴스를 생성할 수 있으며, 각 인스턴스는 독립적으로 상태를 가지고 동작할 수 있습니다. 객체 지향 프로그래밍에서는 클래스를 정의하고 이를 기반으로 인스턴스를 생성하여 객체를 다루는 방식으로 프로그램을 구성합니다.

 

3. 클래스와 인스턴스의 관계를 설명해주세요. 클래스 없이 인스턴스는 생성할 수 있나요?

클래스와 인스턴스는 서로 연관되어 있습니다. 클래스는 객체를 생성하기 위한 템플릿이며, 인스턴스는 클래스의 실체화된 결과입니다. 클래스는 인스턴스를 생성하기 위한 설계도로서 필요하며, 인스턴스는 클래스에 의해 생성됩니다. 즉, 클래스를 정의하고 이를 기반으로 인스턴스를 생성하여 객체를 다루는 것이 객체 지향 프로그래밍의 핵심 개념입니다.

인스턴스를 생성하기 위해서는 반드시 클래스가 필요합니다. 클래스 없이 인스턴스를 직접 생성하는 것은 불가능합니다. 클래스는 인스턴스의 특성과 동작을 정의하는 역할을 하기 때문에, 클래스 없이는 어떤 속성과 동작을 갖는 인스턴스를 생성할  없습니다. 클래스는 인스턴스를 생성하는 기반이 되며, 클래스의 정의에 따라 인스턴스가 생성됩니다.

 

4. 클래스를 설계할 때 어떤 것들을 고려하나요?

  • 목적과 책임: 클래스는 특정한 목적과 책임을 가지고 있어야 합니다. 클래스의 주된 역할과 기능을 명확히 정의하고, 해당 클래스가 어떤 작업을 수행하는지 이해해야 합니다.
  • 응집성: 클래스는 관련된 속성과 메서드를 함께 묶어야 합니다. 응집된 클래스는 유지보수와 확장성이 용이하며, 코드의 가독성을 높일 수 있습니다.
  • 캡슐화: 클래스는 필요한 정보와 동작을 적절하게 캡슐화하여 외부에서 직접적인 접근을 제한해야 합니다. 이를 통해 클래스의 내부 구현을 숨기고, 객체의 상태를 보호하며, 코드의 안정성을 높일 수 있습니다.
  • 상속과 인터페이스: 클래스 설계  상속과 인터페이스를 고려해야 합니다. 상속을 통해 기존 클래스를 확장하거나 재사용할  있으며, 인터페이스를 통해 클래스 간의 계약을 정의할  있습니다.

5. 클래스 상속에 대해 설명해주세요. 이것이 언제 유용하게 사용되나요?

클래스 상속은 한 클래스가 다른 클래스의 특성과 동작을 상속받아 확장하는 것을 말합니다.

상속은 부모 클래스(상위 클래스 또는 슈퍼 클래스)와 자식 클래스(하위 클래스 또는 서브 클래스) 간의 관계를 형성합니다. 자식 클래스는 부모 클래스의 속성과 메서드를 상속받아 사용할 수 있으며, 필요에 따라 새로운 속성이나 메서드를 추가하거나 부모 클래스의 동작을 재정의할 수도 있습니다.

클래스 상속은 코드의 재사용성과 확장성을 높여줍니다. 부모 클래스에서 공통된 속성과 동작을 정의하고, 자식 클래스에서는 이를 상속받아 필요한 수정과 확장을 진행할 수 있습니다. 이는 코드의 중복을 방지하고 유지보수를 용이하게 만들어 줍니다.

클래스 상속은 다음과 같은 경우에 유용하게 사용됩니다:

  • 공통된 기능을 가진 여러 클래스가 있을 때, 중복을 피하기 위해 상속을 사용하여 공통 부분을 부모 클래스로 정의하고 자식 클래스에서 상속받아 사용합니다.
  • 기존 클래스의 기능을 유지하면서 새로운 동작을 추가하고 싶을 때, 기존 클래스를 상속받아 새로운 클래스를 정의합니다.
  • 다형성을 구현하기 위해 사용될 수 있습니다. 여러 클래스가 동일한 부모 클래스를 상속받으면, 부모 클래스 타입으로 다양한 자식 클래스 객체를 다룰 수 있습니다.
  • 코드의 가독성을 높일 수 있습니다. 클래스 간에 상속 관계가 명확하게 되어 있으면, 코드를 이해하기 쉽고 구조적으로 설계할 수 있습니다.
  • 소프트웨어의 변경이나 확장에 대응하기 쉽게 만들어 줍니다. 기존의 클래스를 수정하지 않고도 새로운 동작을 추가하거나 수정할 수 있습니다.

그러나 클래스 상속은 다음과 같은 한계점을 가지고 있습니다:

  • 상속은 강한 결합을 만들어 내므로, 부모 클래스의 변경이 자식 클래스에 영향을 미칠 수 있습니다. 이는 코드의 유연성과 확장성을 제한할 수 있습니다.
  • 단일 상속만 지원되므로, 한 클래스는 하나의 부모 클래스만 상속받을 수 있습니다. 다중 상속이 필요한 경우 복잡성이 증가할 수 있습니다.
  • 상속 구조가 복잡해질 경우 코드의 가독성과 이해가 어려워질 수 있습니다.
  • 부모 클래스의 변경이 자식 클래스에 영향을 주는 경우, 전체 시스템의 테스트와 디버깅이 어려워질 수 있습니다.

따라서 클래스 상속을 사용할 때에는 상속 구조를 신중하게 설계하고, 클래스 간의 결합도를 최소화하는  주의해야 합니다. 또한, 다른 대안으로는 인터페이스를 활용하여 관련 기능을 정의하고 클래스에서 구현하는 방식을 선택할 수도 있습니다.

더보기

클래스 상속은 객체 지향 프로그래밍에서 클래스 간의 관계를 형성하는 개념입니다. 한 클래스가 다른 클래스를 상속받을 때, 상속 받는 클래스는 상위 클래스의 속성과 메서드를 물려받아 사용할 수 있습니다. 이때, 상속을 받는 클래스를 "하위 클래스" 또는 "자식 클래스", 상속을 제공하는 클래스를 "상위 클래스" 또는 "부모 클래스"라고 합니다.

상속의 장점:

  • 코드 재사용: 상위 클래스의 속성과 메서드를 하위 클래스에서 재사용할 수 있습니다. 이는 중복된 코드를 줄이고 유지보수성을 향상시킵니다.
  • 확장성: 하위 클래스에서 상위 클래스의 기능을 확장하거나 수정할 수 있습니다. 새로운 메서드를 추가하거나 상위 클래스의 메서드를 오버라이딩하여 다양한 동작을 구현할 수 있습니다.
  • 다형성: 상속을 통해 다형성 개념을 구현할 수 있습니다. 하위 클래스는 상위 클래스의 타입으로 취급될 수 있으며, 상위 클래스의 인터페이스를 구현한 클래스들을 통합적으로 다룰 수 있습니다.

클래스 상속은 다음과 같은 상황에서 유용하게 사용될 수 있습니다:

  • 공통된 속성과 메서드를 가진 클래스들을 상위 클래스로 추상화하여 코드 중복을 줄일 수 있습니다.
  • 클래스 간의 계층 구조를 표현하여 객체들을 구조적으로 관리할 수 있습니다.
  • 다형성을 활용하여 상위 클래스의 인터페이스를 구현한 여러 클래스들을 통합적으로 다룰 수 있습니다.
  • 상속 관계를 통해 기존 클래스를 확장하여 새로운 클래스를 정의할 수 있습니다.

 

하지만 클래스 상속에는 몇 가지 한계점도 있습니다:

  • 단일 상속: 자바에서는 클래스는 하나의 클래스만 상속할 수 있습니다. 따라서 다중 상속을 지원하지 않습니다.
  • 강한 결합: 상위 클래스의 변경이 하위 클래스에 영향을 미칠 수 있습니다. 상위 클래스의 변경이 필요한 경우, 상속 구조 전체를 수정해야 할 수 있습니다.
  • 상속의 오용: 상속은 클래스 간의 관계를 형성하는 강력한 도구이지만, 오용할 경우 복잡성을 증가시키고 코드를 이해하기 어렵게 만들 수 있습니다. 상속을 적절히 사용해야 합니다.

 

6. 최근 작업했던 프로젝트에서 클래스와 인스턴스를 어떻게 사용했나요?

더보기

최근 작업한 프로젝트에서도 클래스와 인스턴스를 활용하여 객체 지향 프로그래밍의 개념을 적용했습니다. 프로젝트는 웹 애플리케이션으로, 사용자 관리 시스템을 구현하는 데 집중했습니다.

  1. 사용자 클래스(User class): 사용자의 정보를 관리하기 위해 사용자 클래스를 정의했습니다. 이 클래스는 사용자의 이름, 이메일, 비밀번호 등을 저장하고 필요한 작업을 수행할 수 있는 메서드를 가지고 있습니다. 이 클래스를 기반으로 사용자의 정보를 저장하고 관리하는 인스턴스를 생성했습니다.
  2. 회원 가입 기능: 사용자가 회원 가입을 할 때, 사용자 정보를 입력받아 사용자 클래스의 인스턴스를 생성하고 데이터베이스에 저장했습니다. 이를 통해 사용자 정보를 관리하고 인증/인가 작업을 수행할 수 있게 되었습니다.
  3. 로그인 기능: 사용자가 로그인을 할 때, 입력받은 정보를 사용하여 사용자 클래스의 인스턴스를 생성하고 인증 작업을 수행했습니다. 이를 통해 로그인된 사용자의 상태를 유지하고 해당 사용자의 권한에 따라 기능을 제한할 수 있게 되었습니다.
  4. 사용자 관리 기능: 사용자 정보를 수정하거나 삭제하는 기능을 구현할 때에도 사용자 클래스와 해당 인스턴스를 활용했습니다. 사용자의 정보를 업데이트하고 데이터베이스에 반영하는 작업을 수행했습니다.

클래스와 인스턴스를 사용하여 객체를 정의하고 관리함으로써 프로젝트의 구조와 가독성을 높였으며, 유지보수  확장성도 용이하게 만들었습니다. 또한, 객체 지향의 특성을 활용하여 코드를 재사용하고 모듈화할  있었습니다.

 

14. AOP, Interceptor, Filter 의 차이점, Request가 들어올때 거치는 순서, 각 역할들의 장점을 설명해주실 수 있을까요?

1. AOP (Aspect-Oriented Programming):

  • AOP는 관점 지향 프로그래밍의 개념을 기반으로 한다.
  • 주요 목적은 핵심 로직과 횡단 관심사를 분리하여 모듈화하고 중복 코드를 제거하는 것이다.
  • 횡단 관심사를 나타내는 어드바이스(Advice)와 어드바이스를 적용할 지점인 포인트컷(Pointcut)으로 구성된다.
  • 주로 메서드 실행 전후, 예외 발생 시 등의 시점에서 횡단 관심사를 처리한다.

2. Interceptor:

  • Interceptor는 주로 웹 애플리케이션에서 요청을 가로채고 처리하는 역할을 한다.
  • 주로 MVC 패턴에서 컨트롤러 호출 전후, 뷰 렌더링 전후 등의 시점에서 요청을 가로채고 추가적인 작업을 수행한다.
  • Spring Framework에서는 HandlerInterceptor 인터페이스를 구현하여 Interceptor를 사용할 수 있다.
  • 주로 인증, 권한 검사, 로깅 등의 공통적인 작업을 처리한다.

3. Filter:

  • Filter는 웹 애플리케이션에서 요청과 응답을 필터링하고 변환하는 역할을 한다.
  • 서블릿 컨테이너에서 요청을 처리하기 전후에 Filter를 실행하여 요청/응답 데이터에 대한 변환 및 보정 작업을 수행한다.
  • 주로 보안, 인코딩, 캐싱 등의 요청/응답에 대한 공통적인 처리를 담당한다.
  • Servlet Filter 인터페이스를 구현하여 Filter를 사용할 수 있다.

요청이 들어올 때 거치는 순서는 다음과 같다:

  1. Filter: 서블릿 컨테이너에서 요청을 처리하기 전에 Filter가 실행된다.
  2. Interceptor: Filter 이후에 Interceptor가 실행된다.
  3. Controller: Interceptor에서 처리된 후에 컨트롤러가 실행된다.
  4. AOP: 컨트롤러 내부에서 AOP가 적용된 메서드가 호출된다.
더보기

1. 필터(Filter): 클라이언트의 요청이 서블릿 컨테이너에 도달하기 전에 필터가 실행됩니다. 필터는 클라이언트의 요청을 가로채고 사전 처리를 수행할 수 있습니다. 필터 체인에는 여러 개의 필터가 등록되어 있을 수 있으며, 필터는 순차적으로 실행됩니다.

 

2. 인터셉터(Interceptor): 클라이언트의 요청이 컨트롤러에 도달하기 전에 인터셉터가 실행됩니다. 인터셉터는 요청의 전처리 작업을 수행할  있습니다. 인터셉터 역시 여러 개가 등록될  있으며, 순차적으로 실행됩니다.

 

3. 컨트롤러(Controller): 인터셉터에서 요청의 전처리 작업이 완료되면 실제로 요청을 처리하는 컨트롤러가 실행됩니다. 컨트롤러는 클라이언트의 요청에 대한 핵심 비즈니스 로직을 수행합니다

 

4. 인터셉터(Interceptor): 컨트롤러의 실행이 완료된 후에는 인터셉터가 다시 실행됩니다. 이번에는 요청의 후처리 작업을 수행할  있습니다. 인터셉터는 컨트롤러의 실행 결과에 따라 추가적인 작업을 수행할  있습니다.

 

5. 필터(Filter): 최종적으로 응답이 클라이언트로 전달되기 전에 필터가 실행됩니다. 필터는 응답의 후처리 작업을 수행할  있습니다. 필터 체인에 등록된 필터들이 순차적으로 실행되며, 최종적으로 응답이 클라이언트로 전달됩니다.

각각의 장점은 다음과 같다:

  • AOP: 핵심 로직과 횡단 관심사의 분리로 코드의 모듈성, 재사용성, 유지보수성이 증가합니다. AOP를 사용함으로써 횡단 관심사를 한 곳에서 관리할 수 있으며, 코드 중복을 줄이고 가독성을 향상시킬 수 있습니다. 또한, 핵심 로직에 집중할 수 있으므로 코드의 복잡성을 감소시킬 수 있습니다.
  • Interceptor: Interceptor는 웹 애플리케이션에서 요청 전후에 추가 작업을 수행할 수 있습니다. 주로 인증, 권한 검사, 로깅 등의 공통적인 작업을 처리할 수 있습니다. Interceptor를 사용하면 애플리케이션 전반에 걸쳐 일관된 처리를 할 수 있으며, 코드 중복을 피할 수 있습니다.
  • Filter: Filter는 서블릿 컨테이너에서 요청과 응답을 필터링하고 변환하는 역할을 합니다. 주로 보안, 인코딩, 캐싱 등의 요청/응답에 대한 공통적인 처리를 담당합니다. Filter를 사용하면 요청과 응답에 대한 처리를 일관되게 적용할 수 있으며, 여러 개의 Filter를 연결하여 다양한 기능을 적용할 수 있습니다.
더보기

각 역할들의 장점:

필터(Filter): 필터는 서블릿 컨테이너에서 동작하므로 웹 애플리케이션 전체에 적용될 수 있습니다. 필터를 이용하여 요청과 응답에 대한 공통된 작업을 처리할 수 있으며, 보안, 인코딩 변환, 로깅 등의 기능을 구현할 수 있습니다.

인터셉터(Interceptor): 인터셉터는 주로 프레임워크에서 제공되는 기능으로, 컨트롤러의 전후에 공통된 작업을 수행할 수 있습니다. 인증, 로깅, 트랜잭션 처리 등의 공통된 작업을 인터셉터를 통해 처리할 수 있습니다. 인터셉터는 컨트롤러에 직접적으로 영향을 주지 않기 때문에 재사용성과 유지보수의 편의성이 높습니다.

  • 필터(Filter)의 장점:

웹 애플리케이션 전체에 적용될 수 있어 공통된 작업을 처리할 수 있습니다.

요청과 응답에 대한 처리를 다양하게 구현할 수 있습니다.

보안, 인코딩 변환, 로깅 등과 같은 기능을 구현할 수 있습니다.

여러 개의 필터를 체인으로 연결하여 순차적으로 실행할 수 있습니다.

  • 인터셉터(Interceptor)의 장점:

컨트롤러의 전후에 공통된 작업을 수행할 수 있어 코드 중복을 줄일 수 있습니다.

인증, 로깅, 트랜잭션 처리 등과 같은 공통된 작업을 효율적으로 처리할 수 있습니다.

프레임워크에서 제공되는 기능으로 재사용성과 유지보수의 편의성이 높습니다.

여러 개의 인터셉터를 순차적으로 실행할 수 있으며, 필요한 작업을 선택적으로 수행할 수 있습니다.

각각의 역할은  애플리케이션에서 요청의 전처리, 후처리, 공통된 작업 처리  다양한 기능을 수행합니다. 필터는 서블릿 컨테이너에서 동작하며, 클라이언트의 요청과 응답에 대한 처리를 담당합니다. 인터셉터는 주로 프레임워크에서 제공되는 기능으로, 컨트롤러의 전후에 개입하여 공통된 작업을 수행합니다. 이러한 역할들을 적절히 활용하면 코드의 재사용성과 유지보수성을 높일  있으며,  애플리케이션의 기능을 효과적으로 개발할  있습니다.

각각의 역할과 장점을 적절히 조합하여 애플리케이션을 개발하면 코드의 재사용성, 유지보수성, 가독성, 일관성 등을 향상시킬  있습니다.

 

세 가지 모두 공통 관심사를 처리하는 데에 있습니다. Interceptor와 Filter는 Servlet 단위에서 실행되고, AOP는 메서드 앞의 Proxy패턴의 형태로 실행됩니다. 실행 순서로는 Request가 들어오면 Filter -> InterCceptor -> AOP -> Intercepotr -> Filter가 됩니다. 필터의 역할은 요청과 응답을 거르고 정제하는 역할입니다. 일반적으로 인코딩 변환처리, 인증 인가 등의 요청에 대한 처리를 합니다 Interceptor는 요청에 대한 작업 전/후 로 가로챕니다. 스프링과 무관한 자원에 대해 동작을 하고, 보통 로그인, 권한 체크, 프로그램 실행 시간 게산 작업, 로그 확인 등의 업무를 처리합니다 AOP는 주로 '로깅', '트랜잭션', '에러 처리'등 비즈니스단의 메서드에서 조금 더 세밀하게 조정하고 싶을 때 사용 , Interceptor나 Filter와는 달리 메소드 전후의 지점에 자유롭게 설정이 가능합니다.

 

필터는 디스패처 서블릿에 요청이 전달되기 전 / 후에 모든 요청에 대해 부가작업을 처리할 수 있습니다. 인터셉터는 디스패처 서블릿이 컨트롤러를 호출하기 전과 후에 요청과 응답을 참조하거나 가공할 수 있는 기능을 제공합니다. AOP 는 인터셉터나 필터와는 달리 메소드 전후의 지점에 부가작업을 처리할 수 있습니다. 따라서 Request 는 필터 -> 디스패처 서블릿 -> 인터셉터 -> AOP -> 컨트롤러 순서로 들어와 역순으로 나가게 됩니다. 필터는 스프링과 무관하게 전역적으로 처리해야 하는 작업들을 처리할 수 있으며, Request 및 Response 객체를 다른 객체로 바꿀 수 있다는 것이 장점이다. 인터셉터는 스프링의 모든 빈 객체에 접근할 수 있고, Controller로 넘겨주는 Request 및 Response 내부의 값을 바꿔 전달할 수 있다는 장점이 있다. AOP 는 비지니스 단의 메소드 실행 전, 후 등 세밀하게 부가 기능을 조정할 수 있다는 장점이 있다.

블랙박스 테스팅 :
소프트웨어의 내부 구조나 동작 원리를 모르는 상태에서, 사용자의 입장에서 동작을 검사하는 방법입니다. 이러한 방식의 테스팅은 다음과 같은 장점과 단점을 가지고 있습니다.

  • 장점:누구나 테스트를 수행할 수 있습니다. 개발자뿐만 아니라 디자이너, 베타 테스터, 관리자 등 누구나 소프트웨어의 동작을 검사할 수 있습니다.
  • 단점:기능이 증가할수록 테스트의 범위가 증가합니다. 새로운 기능이 추가될 때마다 해당 기능을 테스트해야 하므로 테스트의 양이 계속해서 증가할 수 있습니다.테스트를 수행하는 사람에 따라 테스트 품질이 다를 수 있습니다. 테스트하는 사람의 능력과 경험에 따라 테스트의 정확성과 완전성이 달라질 수 있습니다. 이러한 이유로 QA(Quality Assurance) 직군이 필요한 이유도 있습니다.

개발자 테스트 : 

개발자가 작성한 코드를 검증하기 위해 테스트 코드를 작성합니다. 코드의 예상 동작과 실제 동작을 비교하여 테스트를 수행합니다.

장점:

  • 빠르고 정확한 테스트가 가능합니다. 테스트 코드를 실행하여 예상한 결과와 실제 결과를 비교함으로써 결함을 빠르게 발견할 수 있습니다.
  • 테스트 자동화가 가능합니다. 테스트 코드를 자동으로 실행하여 동작을 검증할 수 있으며, 배포 시 자동화된 테스트가 동작을 검증하는 데 도움이 됩니다.
  • 리팩토링이나 기능 추가 시 편리합니다. 테스트 코드가 있으면 코드 변경에 따른 영향을 빠르게 파악하여 수정할 수 있습니다.

단점:

  • 개발 시간이 더 오래 걸립니다. 테스트 코드를 작성하는 데에는 추가적인 노력과 시간이 필요합니다.
  • 테스트 코드를 유지보수해야 합니다. 코드 변경에 따라 테스트 코드도 함께 수정해야 하므로 유지보수 비용이 증가할 수 있습니다.

단위 테스트 (Unit Test):

개별 모듈 또는 함수 단위로 테스트를 수행합니다. 모듈 내부의 세부 로직과 동작을 검증합니다.

개발 초기 단계부터 개별 모듈을 테스트하며, 빠르게 실행하고 결함을 발견하는 데 유용합니다.

장점:

  • 세밀한 부분까지 테스트가 가능합니다. 모듈의 각각의 기능을 개별적으로 확인하여 결함을 찾을 수 있습니다.
  • 코드 일관성과 안정성을 높일 수 있습니다. 각 모듈이 독립적으로 동작하면서 예상한 대로 작동하는지 확인할 수 있습니다.

단점:

  • 모듈 간의 상호작용을 검증할 수 없습니다. 모듈 간의 연결과 상호작용에서 발생하는 오류를 찾기 어렵습니다.

통합 테스트 (Integration Test):

두 개 이상의 모듈을 연결하여 테스트합니다. 모듈 간의 상호작용과 통합에서 발생하는 오류를 검증합니다.

모듈 간의 상호작용이 복잡하고 중요한 시스템에서 사용되며, 시스템 전체적인 동작을 확인합니다.

장점:

  • 모듈 간의 연결에서 발생하는 에러를 검증할 수 있습니다. 여러 모듈이 함께 동작할 때 발생하는 문제를 찾아낼 수 있습니다.

단점:

  • 테스트 코드 작성이 복잡하고 어려울 수 있습니다. 여러 모듈을 연결하고 상호작용을 검증해야 하기 때문에 테스트 코드 작성이 복잡해질 수 있습니다.
  • 테스트 시나리오의 다양성에 대한 처리가 어렵습니다. 다양한 상황과 조건을 고려하여 테스트를 수행하기 어려울 수 있습니다.

E2E 테스트 (End-to-End Test):

실제 사용자의 관점에서 전체 시스템을 테스트합니다. 사용자의 흐름에 따라 실제 환경에서 애플리케이션이 예상대로 동작하는지 검증합니다.
실제 사용자 경험을 반영하고, 시스템의 완전한 동작을 검증하는데 사용됩니다. 다만, 구축과 유지보수가 복잡하며 시간이 많이 소요될 수 있습니다.

장점:

  • 실제 사용자 환경에서 테스트하기 때문에 실제 상황을 재현할 수 있습니다. 다양한 플랫폼, 단말기, 네트워크 등 다양한 환경에서의 동작을 확인할 수 있습니다.
  • 사용자의 관점에서 테스트하기 때문에 사용자 경험을 중심으로 평가할 수 있습니다.

단점:

  • 다양한 테스트 환경 구성이 어렵습니다. 다양한 단말기, 운영체제, 브라우저 등 다양한 조합에 대한 테스트를 구성하기 어려울 수 있습니다.
  • 테스트 시나리오가 복잡해질 수 있습니다. 사용자 흐름에 따라 다양한 시나리오를 작성하고 관리해야 하므로 테스트 코드 작성이 복잡해질 수 있습니다.

 

단위 테스트 코드 작성 시의 장점과 단점 : 

장점:

  1. 결함을 빨리 찾을 수 있습니다: 단위 테스트 코드는 코드 일부분을 실행하여 오류를 찾아낼 수 있습니다. 따라서 결함을 빨리 찾을 수 있으며 빠르게 수정할 수 있습니다.
  2. 코드 품질 향상: 단위 테스트 코드는 소프트웨어의 코드 품질을 향상시키는 데 도움이 됩니다. 테스트 코드를 작성하면 코드가 보다 정확하고 일관성 있게 작성됩니다.
  3. 유지보수성 개선: 단위 테스트 코드는 코드 변경에 대한 영향도를 파악하며, 변경이 필요한 부분을 빠르게 파악하여 수정할 수 있습니다. 이는 유지보수성을 높이는 데에 큰 도움이 됩니다.
  4. 문서화: 단위 테스트 코드는 코드를 문서화하는 데에도 도움이 됩니다. 각 테스트 케이스는 코드의 사용 예제가 될 수 있으며, 이를 통해 코드 사용 방법을 쉽게 이해할 수 있습니다.

단점:

  1. 추가 작업이 필요합니다: 단위 테스트 코드를 작성하는 데에는 추가적인 노력이 필요합니다. 코드 자체를 작성하는 이외에도 코드 조각에 대한 테스트 케이스를 작성해야 합니다.
  2. 테스트 케이스가 모든 오류를 포착하지 못할 있습니다: 모든 상황을 대비한 테스트 케이스를 작성하기 어렵기 때문에, 단위 테스트는 모든 오류를 포착하지 못할 있습니다.
  3. 작성 비용이 높을 있습니다: 대규모 프로젝트에서는 수많은 단위 테스트 케이스를 작성해야 하므로, 작성 비용이 높아질 있습니다.
  4. 유지보수 비용이 높을 있습니다: 코드 변경 시에는 테스트 케이스도 수정해야 하므로, 유지보수 비용이 높아질 있습니다.

 

TDD(Test-Driven Development)와 BDD(Behavior-Driven Development) :

소프트웨어 개발 방식 중 하나로, 테스트 중심의 개발 프로세스를 강조하지만, 접근법은 약간 다릅니다.
TDD는 코드 수준에서의 테스트 중심 개발이라면, BDD는 시스템의 동작 및 비즈니스 요구사항에 초점을 맞춘 테스트 중심 개발입니다.

TDD (Test-Driven Development) : 

TDD는 작은 단위의 테스트를 먼저 작성하고, 이 테스트를 통과할 수 있도록 코드를 작성하는 방식입니다. 이 방식의 목표는 요구사항이 변경되거나 추가되더라도 소프트웨어의 품질을 유지하는 것입니다.

TDD의 일반적인 절차는 다음과 같습니다:

  1. 먼저 실패하는 단위 테스트를 작성합니다.
  2. 테스트를 통과할 수 있도록 충분한 코드를 작성합니다.
  3. 작성된 코드를 리팩토링합니다.

BDD (Behavior-Driven Development) : 

BDD는 TDD의 한 종류로 볼 수 있지만, BDD는 시스템의 동작에 중점을 둡니다. BDD는 비개발자(예: 비즈니스 분석가, 이해당사자 등)와 개발자가 협업할 수 있도록 테스트 케이스를 자연어 스타일로 작성하는 것을 강조합니다.

BDD의 주요 절차는 다음과 같습니다:

  1. 예상되는 행동에 대해 테스트를 작성합니다. 이는 비즈니스 요구사항을 기반으로 하며, 주로 사용자 스토리와 같은 형식을 사용합니다.
  2. 해당 행동을 구현할 수 있도록 코드를 작성합니다.
  3. 코드를 리팩토링합니다.

결국, TDD BDD 모두 테스트 중심의 개발을 추구하지만, BDD 시스템의 행동에 중점을 두고 비개발자와의 협업을 강조하며, TDD 개발자가 테스트를 기반으로 코드를 작성하도록 강조합니다. 사이의 선택은 프로젝트의 요구사항, 팀의 선호도, 그리고 소프트웨어의 복잡성 등에 따라 달라질 있습니다.

더보기

코드를 먼저 작성하고 테스트를 나중에 하는 접근법은 TDD BDD와는 반대되는 방향입니다. 이런 방식은 코드의 품질을 보장하기 어렵고, 나중에 문제가 발생했을  이를 찾고 수정하는   많은 시간과 노력이 필요하게 됩니다. 따라서 가능하면 TDD BDD 같은 테스트 중심의 개발 방식을 적용하는 것이 좋습니다.

기능을 먼저 구현하고 그 후에 테스트 코드를 작성하는 접근법을 "테스트-후(Test-Last)" 개발 또는 "전통적인" 개발 방법이라고 부릅니다. 이 방법은 전통적인 소프트웨어 개발 프로세스에서 일반적으로 볼 수 있습니다.

이런 접근법에서는 개발자가 요구사항에 따라 기능을 먼저 개발하고, 그 후에 해당 기능을 검증하는 테스트 케이스를 작성합니다. 이 방식의 단점은 코드가 작성된 후에만 테스트가 가능하다는 것이며, 따라서 버그를 일찍 찾아내는 것이 더 어려울 수 있습니다.

반면에, TDD(Test-Driven Development) BDD(Behavior-Driven Development) 같은 테스트-먼저(Test-First) 접근법에서는 테스트 케이스를 먼저 작성하고,  테스트 케이스를 통과하는 코드를 작성합니다.  접근법의 장점은 테스트를 통해 요구사항을 명확히 정의하고, 즉시 피드백을 받을  있다는 것입니다.

  1. 프로젝트의 특성: TDD나 BDD는 많은 이점을 제공하지만, 모든 프로젝트에 적합한 것은 아닙니다. 예를 들어, 프로젝트 요구사항이 자주 변경되지 않고, 프로젝트의 복잡성이 낮을 경우, 전통적인 개발 방법을 사용하는 것이 더 효율적일 수 있습니다.
  2. 팀의 경험과 역량: 팀원들이 TDD나 BDD에 익숙하지 않다면, 이러한 접근법을 도입하는 데 시간과 노력이 필요할 수 있습니다. 이런 경우, 팀의 경험과 역량을 최대한 활용하여 전통적인 개발 방법을 사용하는 것이 더 합리적일 수 있습니다.
  3. 시간 제약: 테스트 주도 개발은 초기에 투자해야 하는 시간이 더 많습니다. 이런 접근법은 장기적으로는 높은 품질의 코드와 유지 관리의 용이성을 제공하지만, 단기적으로는 개발 시간이 늘어날 수 있습니다. 따라서, 시간 제약이 있는 프로젝트에서는 전통적인 개발 방법을 선택할 수 있습니다.

 

테스트 코드를 작성하는 가장 대표적인 방법론 : Given - 준비 When - 실행 Then - 검증

이 방법론은 행동 주도 개발(BDD)에서 널리 사용되는 테스트 패턴입니다.

  • Given: 테스트의 사전 조건을 설정합니다. 이는 테스트가 진행될 환경이나 상태를 의미합니다.
  • When: 테스트의 주체가 특정 행동을 실행하는 단계입니다.
  • Then: When 단계에서의 행동이 주어진 사전 조건(Given) 하에서 예상한 결과를 나타내는지 검증하는 단계입니다.

JUnit : 

JUnit 자바에서 가장 널리 사용되는 테스트 프레임워크로, 개발자가 쉽게 단위 테스트를 작성하고 실행할 있게 도와줍니다. 테스트 케이스는 서로 독립적으로 작성되어야 하며, 이러한 원칙은 테스트의 견고성과 신뢰성을 보장합니다.

 

이상적으로, 각 테스트 케이스는 서로 분리되어야 한다. 이를 위해 가짜 객체(Mock object)를 생성하는 것도 좋은 방법이다.

 

Mock Object :

Mock 객체는 테스트에서 실제 객체의 역할을 대체합니다. Mock 객체를 사용하면 외부 시스템에 의존하지 않고 테스트를 수행할 수 있습니다. 예를 들어, 데이터베이스나 네트워크와 같은 외부 시스템과의 상호작용을 모방할 수 있습니다.

Mock 객체를 만들려면 일반적으로 해당 객체를 구현하는 인터페이스나 클래스를 직접 생성해야 합니다. 경우 모든 메소드의 동작을 직접 구현하거나 오버라이드해야 하므로, 복잡한 객체를 mocking 경우 상당히 번거로울 있습니다.

  • 실제 객체와 겉만 같은 객체입니다. 동일한 클래스명, 함수명
  • 실제 DB 작업은 하지 않습니다.

Mockito :

Mockito 자바에서 가장 널리 사용되는 mocking 프레임워크 하나입니다. Mockito 사용하면 실제 객체 대신 Mock 객체를 쉽게 생성하고, 행동을 정의하고, 검증할 있습니다. 이를 통해 개발자는 시스템의 다른 부분과 독립적으로 특정 부분을 테스트할 있습니다.

  • AOP (Aspect Oriented Programming) 를 통해 부가기능을 모듈화
    • '부가기능'은 '핵심기능'과는 관점(Aspect), 관심이 다름
    • 따라서 '핵심기능'과 분리해서 '부가기능' 중심으로 설계, 구현 가능
더보기

AOP(Aspect-Oriented Programming)은 소프트웨어 개발에서 모듈성과 관점 분리를 지원하기 위한 프로그래밍 패러다임입니다. AOP는 핵심 로직과 횡단 관심사(Cross-cutting Concerns)를 분리하여 개발하는 방법을 제공합니다.

횡단 관심사란, 여러 모듈에서 공통으로 발생하는 기능 또는 요구사항을 말합니다. 예를 들어, 로깅, 트랜잭션 관리, 보안 등이 횡단 관심사에 해당합니다. 이러한 횡단 관심사는 여러 모듈에 흩어져 있어 코드 중복과 유지보수의 어려움을 초래할 수 있습니다.
AOP는 이러한 횡단 관심사를 핵심 로직과 분리하여 관리합니다. 핵심 로직은 프로그램의 주요 기능을 담당하는 부분이며, 횡단 관심사는 부가적인 기능을 제공합니다. AOP는 이러한 횡단 관심사를 모듈화하여 한 곳에서 관리하고, 핵심 로직에 영향을 주지 않으면서 필요한 시점에 적용할 수 있도록 해줍니다.

 

AOP의 주요 개념은 다음과 같습니다:

  1. Aspect(관점):
    횡단 관심사를 모듈화한 것을 말합니다. 로깅, 트랜잭션 관리, 보안 등이 Aspect의 예시입니다.
    Aspect는 어드바이스와 포인트컷으로 구성됩니다.
  2. Join point(조인 포인트):
    프로그램 실행 중에 Aspect가 적용될 수 있는 특정한 시점을 말합니다. 메서드 호출, 예외 발생 등이 조인 포인트의 예시입니다.
  3. Advice(어드바이스):
    조인 포인트에서 수행되는 횡단 관심사의 구체적인 구현을 말합니다.
    @Before, @After, @Around 등의 어노테이션을 사용하여 어드바이스를 정의할 수 있습니다.
  4. Pointcut(포인트컷):
    어드바이스가 적용될 조인 포인트를 선택하는 방식을 말합니다.
    표현식이나 패턴을 사용하여 포인트컷을 정의할 수 있습니다.
  5. Weaving(위빙):
    Aspect를 핵심 로직에 적용하는 과정을 말합니다. 컴파일 시점, 클래스 로딩 시점, 실행 시점 등에 위빙이 수행될 수 있습니다.

AOP의 장점과 활용 예시를 살펴보겠습니다:

장점:

  1. 모듈성과 재사용성: 횡단 관심사가 분리되어 모듈화되기 때문에 코드 중복을 줄이고 재사용성을 높일 수 있습니다.
  2. 관점 분리: 핵심 로직과 횡단 관심사가 분리되어 코드를 읽고 이해하기 쉽습니다. 이로 인해 코드의 가독성과 유지보수성이 향상됩니다.
  3. 유연성과 확장성: 횡단 관심사를 독립적으로 변경하거나 추가할 수 있어 유연한 시스템 구조를 구성할 수 있습니다.
  4. 중복 코드 제거: 횡단 관심사를 하나의 모듈로 관리하므로 중복된 코드를 제거할 수 있습니다.

AOP의 활용 예시:

  1. 로깅: 핵심 로직에서 발생하는 이벤트를 로그로 남기는 작업을 횡단 관심사로 분리할 수 있습니다. 예를 들어, 메서드 실행 전후에 로그를 출력하는 어드바이스를 작성하여 로깅을 자동화할 수 있습니다.
  2. 트랜잭션 관리: 데이터베이스 작업과 같은 트랜잭션 처리를 횡단 관심사로 분리하여 중복 코드를 제거하고, 트랜잭션의 시작과 종료를 자동으로 처리할 수 있습니다.
  3. 보안: 인증 및 권한 검사와 같은 보안 관련 작업을 횡단 관심사로 분리하여 애플리케이션 전반에 걸쳐 일관된 보안 기능을 제공할 수 있습니다.
  4. 성능 모니터링: 핵심 로직의 실행 시간을 측정하거나 메서드 호출 횟수를 기록하는 등의 작업을 횡단 관심사로 분리하여 애플리케이션의 성능을 모니터링할 수 있습니다.

이 외에도 AOP는 다양한 도메인에서 유용하게 활용될 수 있습니다. Spring Framework와 같은 프레임워크에서 AOP를 지원하고 있으며, 자바의 어노테이션을 이용한 AOP 설정이 일반적으로 사용됩니다.

  • 스프링 AOP 어노테이션
  1. @Aspect:
  • @Aspect 어노테이션은 스프링 빈 (Bean) 클래스에만 적용되며, 해당 클래스가 Aspect 역할을 수행함을 나타냅니다.
  • Aspect는 어드바이스와 포인트컷을 포함한 AOP 구성 요소들을 정의하고 관리하는 역할을 합니다.
  • Aspect 클래스는 보통 @Component 어노테이션과 함께 사용되어 스프링 컨테이너에 등록됩니다.
  1. 어드바이스 종류:
  • 어드바이스는 AOP에서 실행될 시점을 지정하는 역할을 합니다.
  • @Around, @Before, @After, @AfterReturning, @AfterThrowing 등이 있습니다.
  • @Around: '핵심기능' 수행 전과 후 (@Before + @After)
  • @Before: '핵심기능' 호출 전 (ex. Client 의 입력값 Validation 수행)
  • @After: '핵심기능' 수행 성공/실패 여부와 상관없이 언제나 동작 (try, catch 의 finally() 처럼 동작)
  • @AfterReturning: '핵심기능' 호출 성공 시 (함수의 Return 값 사용 가능)
  • @AfterThrowing: '핵심기능' 호출 실패 시. 즉, 예외 (Exception) 가 발생한 경우만 동작 (ex. 예외가 발생했을 때 개발자에게 email 이나 SMS 보냄)
  1. 포인트컷:
  • 포인트컷은 AOP에서 어드바이스가 실행될 장소를 지정하는 역할을 합니다.
  • 포인트컷 표현식을 사용하여 메서드, 클래스, 패키지 등을 지정할 수 있습니다.
  • 포인트컷 표현식은 execution() 패턴을 사용하여 지정됩니다.
  • execution(modifiers-pattern? return-type-pattern declaring-type-pattern? method-name-pattern(param-pattern) throws-pattern?) 형식을 따릅니다.
  • modifiers-pattern은 접근 제한자를 지정(public, private, *)하며,
    return-type-pattern은 반환 타입을 지정(void, String, List<String>, *)합니다.
  • declaring-type-pattern은 클래스를 지정(com.sparta.springcore.controller.* - controller 패키지의 모든 클래스에 적용, com.sparta.springcore.controller.. - controller 패키지 및 하위 패키지의 모든 클래스에 적용)하고, method-name-pattern은 메서드 이름을 지정합니다.
  • param-pattern은 메서드의 파라미터를 지정합니다. (*)는 1개의 인수를 의미하고, (..)은 0개 이상의 인수를 의미합니다.
더보기
  • () - 인수 없음
  • (*) - 인수 1개 (타입 상관없음)
  • (..) - 인수 0~N개 (타입 상관없음)
  • @Pointcut 어노테이션을 사용하여 포인트컷을 정의하고 재사용할 수 있습니다. 또한, 여러 개의 포인트컷을 결합할 수 있습니다.

 

  • 트랜잭션 :
    데이터베이스에서 데이터에 대한 하나의 논리적 실행단계 ACID (원자성, 일관성, 고립성, 지속성)는 데이터베이스 트랜잭션이 안전하게 수행된다는 것을 보장하기 위한 성질을 가리키는 약어
    • 원자성(Atomicity): 트랜잭션 내의 모든 작업이 성공적으로 수행되거나 아니면 전혀 수행되지 않아야 합니다. 트랜잭션 중간에 어떤 실패도 발생하면 모든 작업이 롤백되어야 합니다.
    • 일관성(Consistency): 트랜잭션이 성공적으로 완료되면 항상 일관된 데이터베이스 상태를 보장해야 합니다. 즉, 트랜잭션이 실행되기 이전과 후의 데이터 상태는 일관성을 유지해야 합니다.
    • 고립성(Isolation): 동시에 실행되는 트랜잭션들이 서로 영향을 주지 않아야 합니다. 트랜잭션이 실행 중인 상태에서는 그 트랜잭션 내의 변경사항을 다른 트랜잭션에서는 볼 수 없어야 합니다.
    • 지속성(Durability): 트랜잭션이 성공적으로 완료된 후, 그 결과는 영구적으로 반영되어야 합니다. 시스템이 다운되거나 문제가 발생하더라도, 완료된 트랜잭션의 결과는 보존되어야 합니다.
    이러한 트랜잭션의 ACID 원칙은 데이터의 정확성과 일관성을 보장하며, 데이터베이스 시스템에서 중요한 역할을 합니다.

 

  • 트랜잭션의 특징 : 
    • 더 이상 쪼갤 수 없는 최소단위의 작업입니다.
    • 하나의 최소 단위의 작업에 여러가지 데이터 변경을 넣으면, 모두 저장되거나, 아무 것도 저장되지 않거나를 보장합니다
    • 모두 성공 시 ⇒트랜잭션 Commit, 중간에 하나라도 실패 시 ⇒ 트랜잭션 Rollback

 

  • 데이터베이스를 더 안전하게 관리하려면? (Primary, Replica)
  • DB 운영방식
    • 웹 서비스에서 DB 에 담겨있는 Data 는 가장 중요한 리소스 입니다.
      • 회원 정보
      • 서비스 이용 정보
    • DB 훼손 가능성
      • DB 도 결국 물리적인 HDD (하드 디스크) 에 존재
      • DB 가 저장된 하드디스크 고장
      • DB 가 저장된 컴퓨터 고장
    • 일반적으로는 DB 를 2대 이상 운영합니다.(읽기 전용 DB와 쓰기 전용 DB를 분리)
    • 쓰기 전용 DB를 카피(사실 데이터 변경을 추적)하는 읽기 전용 DB들을 다수 두는 방식으로 해결. 부하도 분산되고 안전하게 운영이 가능
  • Primary / Replica 운영방식 :
    데이터베이스의 안정성과 데이터의 안전을 위해 일반적으로 기본(Primary) 데이터베이스와 복제(Replica) 데이터베이스를 구축하고 운영하는 방식을 사용합니다.
    • Primary (쓰기 전용): 모든 쓰기 작업 (Create, Update, Delete)은 Primary DB에 직접 수행됩니다. Primary DB에 쓰여진 데이터는 Replica DB로 복제됩니다.
    • Replica (읽기 전용): 읽기 작업은 복제된 DB에서 수행됩니다. 이렇게 함으로써, 부하를 분산시키고, Primary DB에 문제가 발생했을 경우 데이터의 손실을 방지할 수 있습니다.
    스프링 프레임워크에서는 @Transactional(readOnly = true) 어노테이션을 사용하여 읽기 전용 트랜잭션을 선언할 수 있습니다. 이렇게 하면 해당 트랜잭션은 읽기 전용 DB(Replica)에서 수행되게 됩니다.따라서 Primary - Replica 방식은 데이터의 안정성을 보장하며, 동시에 DB 작업의 부하를 분산시키는 효과적인 방법입니다.
  • 또한, 이런 Primary - Replica 구조는 Failover라는 개념과도 밀접한 관련이 있습니다. Primary DB에 장애가 발생하면, 하나의 Replica DB가 자동으로 Primary DB의 역할을 수행하게 됩니다. 이를 통해 서비스의 중단 없이 데이터베이스를 안정적으로 운영할 수 있습니다.
  1. 객체지향 프로그래밍 (OOP): 스프링 프레임워크는 객체지향 프로그래밍에 기반하며, 서버 개발 일반적으로 3계층 (Controller, Service, Repository) 나눠서 개발합니다. 이를 통해 코드의 재사용성이 높아지며, 필요한 기능들을 모듈화하여 쉽게 활용할 있습니다
    모듈화가 잘 되어 있어, 필요한 모듈들만 레고처럼 조립하여 사용 가능 ex) 스프링 시큐리티, 스프링 타임리프, 스프링 Data JPA 등
  2. DI (의존성 주입): 스프링은 DI 지원하여 강한 결합을 완화시키고, 코드의 유연성을 높여줍니다.
  3. 비즈니스 로직에 집중: 스프링은 프레임워크의 다른 부분들이 클라이언트와 데이터베이스 간의 통신을 처리하므로, 개발자는 비즈니스 로직에 집중할 있습니다.
  4. : AOP 지원, 테스트 코드 작성의 용이성, 20 넘게 발전해온 신뢰성 있는 프레임워크 등도 스프링 프레임워크의 중요한 특징입니다.
더보기

Enterprise applications 개발 편의성 제공(스프링은 기업용 애플리케이션의 요구사항 해결에 초점을 맞춘 프레임워크)

 

스프링 프레임워크는 어떤 특징 혹은 장점을 가지고 있나요? (3가지 이상)

  1. 의존성 주입(Dependency Injection, DI): 스프링은 DI 통해 객체 간의 의존성을 외부에서 관리합니다. 이를 통해 객체 간의 결합도를 낮추고, 유연성과 테스트 용이성을 높일 있습니다.
  2. 프레임워크의 모듈화: 스프링은 여러 모듈로 구성되어 있어 필요한 기능만 선택하여 사용할 있습니다. 예를 들어, 스프링 MVC 이용한 개발, 스프링 Security 이용한 보안, 스프링 Data JPA 이용한 데이터베이스 접근 등이 있습니다.
  3. 트랜잭션 관리: 스프링은 선언적 트랜잭션 관리를 지원하여, 코드 레벨에서 트랜잭션 처리를 필요 없이 트랜잭션을 설정하고 관리할 있습니다. 이를 통해 코드의 복잡도를 줄일 있습니다.
  4. AOP(Aspect-Oriented Programming) 지원: 스프링은 AOP 지원하여, 비즈니스 로직과 각종 공통 기능(트랜잭션, 로깅, 보안 ) 분리하여 개발할 있습니다. 이를 통해 코드의 재사용성과 유지보수성을 높일 있습니다.
  5. 테스트 용이성: 스프링은 JUnit 같은 테스팅 프레임워크와 통합되어 테스트하기 쉬운 구조를 가지고 있습니다. DI AOP 지원으로 단위 테스트와 통합 테스트를 편리하게 수행할 있습니다.
  6. 다양한 데이터 접근 기술 지원: 스프링은 JDBC, JPA, Hibernate 다양한 데이터 접근 기술을 지원하며, 이에 대한 템플릿을 제공합니다. 이를 통해 데이터베이스 연동 작업을 쉽게 처리할 있습니다.

 

스프링에서 DI (의존성 주입) 를 사용하는 이유가 무엇인가요? 예를 통해 설명 해 보세요.

 

  1. 결합도를 낮춤: DI 사용하면 객체들 간의 결합도가 낮아집니다. 결합도가 낮다는 것은 하나의 객체가 다른 객체에 대해 알아야 하는 정보가 적다는 뜻입니다. 이로 인해 코드의 수정이 필요한 경우에도 다른 코드에 주는 영향을 최소화할 있습니다.
  2. 유연성과 확장성 향상: DI 사용하면 인터페이스를 기반으로 프로그래밍할 있어, 코드의 유연성과 확장성이 향상됩니다. 필요에 따라 다른 구현체를 주입할 있으므로, 코드 수정 없이도 기능의 변경이나 추가가 가능합니다.
  3. 테스트 용이성 증가: DI 사용하면 테스트 시에 테스트 대상 객체의 의존 객체를 쉽게 대체할 있습니다. 이를 통해 테스트하기 어려운 요소(외부 API, DB ) 가짜 객체(Mock Object) 대체하여 테스트를 수월하게 진행할 있습니다.
더보기

의존성을 주입함으로써 높은 결합도를 느슨하고 유연하게 한다.(느슨한 결합(loose coupling)

클래스 A에서 B라는 객체를 사용해야 할 때 B 인스턴스를 생성하면(B b = new B()) 두 객체는 높은 결합도를 가지게 된다. 반면, A 클래스에서 B가 필요하다는 정의만 내리고(B b;) B 객체를 주입하는 것은 외부에서 이루어지도록 하면 결합도를 느슨하게 할 수 있다.

객체를 주입하는 방법에는 3가지가 있는데 필드 주입, setter 주입, 생성자 주입이다.

여기서 생성자 주입을 가장 권장 한다. 그 이유로는

  1. 필드 주입으로는 순환 의존성을 파악하기 어려움. 생성자 주입을 하게 되면 서버 기동 시 순환 의존성을 가지는 요소들을 파악할 수 있게 에러메시지를 표시하면서 서버 기동이 되지 않는다.
  2. 불변성(Immutability) 보장 : 필드 주입은 final을 선언할 수 없지만 생성자 주입은 final을 선언함으로써 객체가 변하지 않도록 방지해준다. 객체의 안정성을 높힌다. 
  3. 단일 책임 원칙 준수(Single Responsibility Principle) : 생성자에서 주입 받는 의존성이 너무 많을 경우 해당 클래스가 너무 많은 역할을 담당하고 있는지 여부 체크

 

스프링 프레임워크의 핵심은 IoC(Inversion of Control)와 DI(Dependency Injection)로, 이 두 가지 개념이 스프링의 빈 생명 주기 및 빈 간의 관계 설정에 크게 영향을 미칩니다.

 

IoC(Inversion of Control) : 

'제어의 역전'이라는 의미로, 객체의 생성과 생명 주기를 개발자가 아닌 프레임워크가 담당하는 개념입니다. 일반적으로 개발자는 직접 new 키워드를 사용해 객체를 생성하고, 객체간의 의존성을 설정합니다. 그러나 이러한 접근 방식은 코드가 매우 결합력이 높아져 유지 보수가 어렵다는 단점이 있습니다. 이를 해결하기 위해 IoC는 객체의 생성과 생명 주기를 프레임워크가 담당하도록 하여 코드의 결합력을 줄이고 유지 보수성을 향상시킵니다.

DI(Dependency Injection) :

'의존성 주입'이라는 의미로, 객체간의 의존성을 코드 내에서 직접 명시하는 것이 아니라 외부(즉, 프레임워크)에서 주입하는 방식을 의미합니다. DI는 IoC의 구현 방법 중 하나로, 클래스 내부에서 생성자나 setter 메소드를 통해 의존성을 주입받습니다.

스프링에서는 이러한 IoC와 DI 개념을 이용해 BeanFactory나 ApplicationContext와 같은 컨테이너를 통해 객체(Bean)의 생명 주기를 관리하고, Bean 간의 의존성을 주입합니다.

 

Singleton Bean : 

스프링에서 Bean은 기본적으로 Singleton Scope를 가지며, 이는 동일한 타입의 Bean이 요청되면 항상 같은 인스턴스를 반환함을 의미합니다. 이렇게 함으로써 메모리 효율성과 성능 최적화를 도모할 수 있습니다. 하지만, Singleton Bean은 상태를 가지면 안 되며, 그렇지 않으면 동시성 문제가 발생할 수 있습니다. 이러한 이유로, Singleton Bean은 일반적으로 무상태(stateless)로 설계되어야 합니다.

하지만 모든 상황에서 Singleton Scope가 최선은 아닙니다. 때로는 Prototype, Request, Session 등 다른 Scope를 가진 Bean이 필요할 수 있습니다. 이는 애플리케이션의 요구사항에 따라 달라집니다.

따라서 스프링 프레임워크는 개발자로 하여금 복잡한 객체 생성 및 생명 주기 관리, 그리고 의존성 설정 작업으로부터 벗어나게 하여, 개발자가 비즈니스 로직에 집중할 수 있게 합니다.

또한, 스프링 컨테이너는 개발자가 설정한 메타데이터(@Annotation, XML, Java Config 등)를 바탕으로 이러한 작업을 수행합니다. 이는 개발자가 의존성 관계를 코드 상에서 직접 작성하는 것이 아니라 외부에서 설정하게 함으로써, 코드의 결합도를 낮추고 유연성을 향상시킵니다.

 

스프링에서는 BeanFactory와 ApplicationContext 등 다양한 컨테이너가 제공되며, 이 중에서는 특히 ApplicationContext가 가장 일반적으로 사용됩니다. ApplicationContext는 BeanFactory의 모든 기능을 포함하고 있을 뿐만 아니라, 다양한 엔터프라이즈 지원 기능을 추가적으로 제공합니다. 이러한 기능에는 국제화 처리, 이벤트 발행, 리소스 로딩 등이 포함됩니다.

결국, 스프링 프레임워크의 핵심 기능은 이러한 IoC와 DI를 이용하여 개발자 대신에 복잡한 객체 생성 및 생명주기 관리 작업을 수행하고, 이를 통해 코드의 결합도를 낮추고 유연성을 향상시키는 것입니다. 이로써 개발자는 더 깔끔하고 유지보수가 용이한 코드를 작성할 수 있게 됩니다.

 

 

 

    • @Component: 스프링이 관리하는 Bean임을 나타내는 기본 주석입니다. @Component를 사용하면 스프링이 해당 클래스의 객체를 생성하고 관리하게 됩니다. @Controller, @Service, @Repository는 @Component를 특화시킨 주석으로 각각의 계층을 나타냅니다.
    • @Autowired: 스프링에서 의존성 주입(Dependency Injection) 처리하는 사용되는 주석입니다. 주석을 사용하면 스프링이 자동으로 해당 필드나 메소드에 적합한 Bean 찾아 주입합니다.

스프링 3계층 아키텍처란, 일반적으로 웹 애플리케이션을 개발할 때 사용되는 디자인 패턴입니다. 이것은 크게 Presentation Layer (프레젠테이션 계층), Business Logic Layer (비즈니스 로직 계층), 그리고 Data Access Layer (데이터 액세스 계층)로 구분됩니다.

  • Presentation Layer는 사용자 인터페이스와 관련된 코드를 담당합니다. 사용자의 요청을 받아 처리하고, 그 결과를 사용자에게 보여주는 역할을 합니다. 스프링에서는 일반적으로 Controller가 이 역할을 합니다.
  • Business Logic Layer는 애플리케이션의 핵심 비즈니스 로직을 담당합니다. 이 계층은 데이터의 가공과 처리를 담당하며, 스프링에서는 이를 Service라고 합니다.
  • Data Access Layer는 데이터베이스와의 연동을 담당합니다. CRUD(Create, Read, Update, Delete) 연산 등 데이터베이스와 관련된 로직을 처리하며, 스프링에서는 Repository 또는 DAO(Data Access Object)가 이 역할을 합니다.

Annotation(@)은 코드에 메타데이터를 추가하는 방법입니다. 스프링에서는 클래스, 메소드, 변수 등에 여러가지 주석을 달 수 있으며, 이 주석은 스프링 컨테이너에 의해 해석되어 해당 요소의 동작을 결정하게 됩니다. 예를 들어, @Controller, @Service, @Repository 등의 주석은 각각의 클래스가 프레젠테이션 계층, 비즈니스 로직 계층, 데이터 액세스 계층을 담당함을 나타냅니다.

 "스프링 3계층 Annotation(@)"은 스프링의 3계층 아키텍처에서 각 계층을 표현하는 Annotation을 의미합니다. 

      • @Controller: 프레젠테이션 계층을 나타내며, 사용자 요청을 처리하는 클래스에 사용됩니다.
      • @Service: 비즈니스 로직 계층을 나타내며, 핵심 비즈니스 로직을 처리하는 클래스에 사용됩니다.
      • @Repository: 데이터 액세스 계층을 나타내며, 데이터베이스 연동 로직을 처리하는 클래스에 사용됩니다.
      • JpaRepository 인터페이스를 상속받는 클래스에서는 @Repository 주석을 명시적으로 사용할 필요가 없습니다. 스프링 Data JPA 이를 자동으로 인식하기 때문입니다.

이러한 주석은 스프링이 해당 클래스를 관리하도록 지시하며, 그러면 스프링은 해당 클래스를 스프링 컨테이너에 등록하고 필요에 따라 객체를 생성하거나 주입하는 등의 작업을 수행하여 코드를 보다 명확하고 가독성 있게 만들고, 스프링에서 제공하는 기능을 더욱 편리하게 사용할 있도록 돕습니다.

 

1. jpa 가 무엇인가요 ? 

JPA(Java Persistence/영속 API)는 자바 어플리케이션에서 관계형 데이터베이스를 사용하는 방식을 정의한 인터페이스입니다. 이는 자바의 ORM(Object Relational Mapping) 기술 표준으로, 객체와 테이블을 매핑함으로써 객체 지향적인 코드 작성을 가능하게 하고, SQL 작성 없이 데이터베이스 데이터를 다룰 수 있게 합니다.

JPA는 인터페이스로서 자체적으로는 아무런 기능도 없습니다. 실제 기능을 제공하는 것은 JPA를 구현한 ORM 프레임워크들이며, 대표적으로는 Hibernate, EclipseLink, OpenJPA 등이 있습니다. 스프링에서는 이런 JPA 구현체를 이용하여 데이터베이스를 쉽게 다룰 수 있도록 Spring Data JPA를 제공하고 있습니다. JPA는 다음과 같은 특징들을 가지고 있습니다:

  1. 객체 중심 개발: JPA를 사용하면 개발자는 SQL Query를 직접 다루지 않고, 객체를 통해 데이터를 처리할 수 있습니다. 이렇게 해서 객체 지향적인 설계를 보다 쉽게 할 수 있게 됩니다.
  2. 데이터베이스 독립성: JPA는 데이터베이스마다 다른 SQL 문법에 의존하지 않습니다. 즉, 데이터베이스가 바뀌더라도 JPA 코드를 변경할 필요가 없습니다.
  3. 자동 생성된 SQL: 개발자가 직접 SQL을 작성할 필요 없이, JPA가 자동으로 SQL을 생성하고 실행합니다.
  4. 자동 DDL 생성: 개발자가 직접 테이블을 생성할 필요 없이, 엔티티 클래스에 선언된 정보를 바탕으로 JPA가 테이블을 생성합니다.

JPA 이러한 장점들 외에 코드의 가독성과 유지보수에도 도움을 주지만, 성능 최적화나 복잡한 쿼리 작성 등에서는 직접 SQL 다루는 것에 비해 제약 사항이 있을 있습니다. 

 

2. JPA는 왜 사용하나요? ORM에 대해 설명해보세요.

JPA(Java Persistence API)를 사용하는 주요 이유는 객체 지향 프로그래밍과 관계형 데이터베이스간의 패러다임 불일치를 해결하기 위함입니다. 객체 지향 프로그래밍은 상속, 다형성, 데이터와 기능의 캡슐화 등을 제공하지만, 관계형 데이터베이스는 이러한 개념들을 지원하지 않습니다. JPA와 같은 ORM(Object-Relational Mapping) 프레임워크를 사용하면 이러한 차이를 보다 쉽게 극복할 수 있습니다.

ORM은 객체와 관계형 데이터베이스의 데이터를 서로 매핑하는 기술입니다. 이는 객체 지향 프로그래밍 언어에서 사용하는 객체를 관계형 데이터베이스의 테이블에 매핑함으로써, 개발자가 SQL 쿼리를 직접 작성하지 않고도 데이터베이스 연산을 수행할 수 있게 해줍니다.

ORM과 JPA를 사용하면 다음과 같은 이점을 얻을 수 있습니다:

  1. 개발 생산성 향상: 개발자가 직접 SQL을 작성하지 않아도 되므로, 개발 과정이 단순화되고 개발 시간이 줄어듭니다. 이는 특히 CRUD(Create, Read, Update, Delete) 연산에서 큰 장점으로 작용합니다.
  2. 코드의 가독성 향상: SQL 쿼리 대신 객체 지향적인 코드를 작성할 수 있으므로, 코드의 가독성과 유지 관리성이 향상됩니다.
  3. 데이터베이스 독립성: JPA는 특정 데이터베이스에 종속적이지 않습니다. 즉, 데이터베이스가 변경되더라도 JPA를 사용하는 코드를 수정할 필요가 없습니다.
  4. 표준화: JPA는 자바의 ORM 기술에 대한 표준 스펙입니다. 이는 다양한 JPA 구현체 간에 일관된 API를 제공하므로, 구현체를 변경하더라도 코드의 변화를 최소화할 수 있습니다.

ORM이나 JPA 사용하면서 주의해야할 점은, 성능 최적화나 복잡한 쿼리 작성 등에서는 SQL 직접 다루는 것에 비해 제약 사항이 있을 있습니다. 따라서 이러한 특성을 이해하고, 필요에 따라서는 JPA SQL 적절히 혼용하는 것이 중요합니다.

 

2-1. ORM, JPA, Spring Data JPA 를 구분해서 설명해 보세요.

  • ORM (Object-Relational Mapping): ORM은 객체 지향 프로그래밍 언어에서 사용하는 객체와 관계형 데이터베이스의 데이터를 서로 매핑하는 기술입니다. ORM은 개발자가 SQL 쿼리를 직접 작성하지 않고도 데이터베이스 연산을 수행할 수 있게 하여, 개발 생산성을 향상시키고 코드의 가독성을 높입니다.
  • JPA (Java Persistence API): JPA는 자바 언어를 위한 ORM 기술 표준입니다. JPA는 인터페이스의 모음으로, 여러 ORM 프레임워크가 JPA 표준을 준수하도록 하여 개발자가 다양한 ORM 프레임워크를 쉽게 교체하거나 사용할 수 있게 해줍니다. Hibernate, EclipseLink, OpenJPA 등이 JPA를 구현한 대표적인 프레임워크입니다.
  • Spring Data JPA: Spring Data JPA는 스프링 프레임워크에서 제공하는 모듈 중 하나로, JPA를 좀 더 편리하게 사용할 수 있게 해주는 기능을 제공합니다. Spring Data JPA를 사용하면, 개발자가 JPA를 직접적으로 사용하는 대신, 간단한 인터페이스 정의만으로 Repository를 구현할 수 있습니다. 이로 인해 개발 과정이 더욱 간단해지고, 코드의 복잡성이 줄어듭니다.

요약하면, ORM 객체와 관계형 데이터베이스 사이의 매핑을 처리하는 기술입니다. JPA ORM 자바 언어에서 사용할 있도록 표준화한 것이며, Hibernate 등이 이를 구현한 프레임워크입니다. Spring Data JPA JPA 쉽게 사용할 있도록 스프링에서 제공하는 추상화된 레이어입니다.

 

3. JPA에서 객체와 테이블 간의 매핑(mapping)에 사용되는 어노테이션에 대해 설명해보세요.

  1. @Entity: 클래스가 엔티티임을 선언하고, 클래스의 인스턴스가 데이터베이스에 저장될 있음을 나타냅니다.
  2. @Table: 엔티티가 매핑될 데이터베이스 테이블을 지정합니다. @Table 생략하면 클래스 이름이 테이블 이름으로 사용됩니다.
  3. @Id: 엔티티의 기본 (primary key) 나타내는 필드에 선언합니다.
  4. @GeneratedValue: 기본 키의 생성 전략을 지정합니다. AUTO, IDENTITY, SEQUENCE, TABLE 다양한 전략을 선택할 있습니다.
  5. @Column: 필드가 매핑될 데이터베이스 컬럼을 지정합니다. @Column 생략하면 필드 이름이 컬럼 이름으로 사용됩니다.
  6. @OneToOne, @OneToMany, @ManyToOne, @ManyToMany: 엔티티 간의 관계를 나타냅니다.
  7. @JoinColumn: 관계를 맺는 필드를 데이터베이스의 외래 키로 매핑합니다.
  8. @Embeddable, @Embedded: 임베디드 타입(embedded type) , 타입(Value Type) 정의하고 사용할 쓰입니다.
  9. @Transient: 해당 필드가 데이터베이스에 매핑되지 않도록 합니다. 필드는 데이터베이스에 저장되거나 검색되지 않습니다.
  10. @Temporal: 날짜 타입을 매핑할 쓰입니다. Date, Calendar 타입을 매핑할 때는 반드시 어노테이션을 사용해야 합니다.
더보기

@GeneratedValue 어노테이션은 데이터베이스에서 자동으로 생성되는 ID 값을 처리하는 방법을 설정합니다. 이 어노테이션을 사용하는 방법에는 AUTO, IDENTITY, SEQUENCE, TABLE 등 4가지가 있습니다.

  1. AUTO: 이것은 기본값입니다. 데이터베이스를 보고 알아서 선택해 줍니다. 예를 들어 MySQL 사용하면 IDENTITY 방식을, Oracle 사용하면 SEQUENCE 방식을 선택해 줍니다.
  2. IDENTITY: 방식은 데이터베이스에 데이터를 저장할 때마다, 데이터베이스가 자동으로 ID 값을 만들어줍니다. 예를 들어, MySQL에서는 AUTO_INCREMENT 기능이 이에 해당합니다.
  3. SEQUENCE: 방식은 데이터베이스에 미리 정해진 숫자 순서(시퀀스) 따라 ID 값을 만들어줍니다. 주로 Oracle 데이터베이스에서 사용됩니다.
  4. TABLE: 방식은 별도의 테이블을 만들어 테이블을 사용해 ID 값을 만들어 줍니다. 방식은 모든 데이터베이스 시스템에서 사용할 있지만, 별도의 테이블을 관리해야 하는 번거로움이 있습니다.

 

4. JPA에서 연관관계 매핑이란 무엇인가요? 이를 사용하는 이유는 무엇인가요?

JPA에서의 연관관계 매핑은 데이터베이스의 테이블 간의 관계를 객체의 참조로 표현하는 것입니다. 관계형 데이터베이스에서는 외래 키를 사용하여 테이블 간의 관계를 표현하지만, 객체 지향 프로그래밍에서는 객체 참조를 사용하여 객체 간의 관계를 표현합니다. 연관관계 매핑은 이 두 가지 관계 표현 방식을 연결하는 역할을 합니다.

JPA에서는 @ManyToOne, @OneToMany, @OneToOne, @ManyToMany와 같은 어노테이션을 사용해 다음 네 가지 종류의 연관관계를 매핑할 수 있습니다.

  • @ManyToOne / @OneToMany: 하나의 객체가 다른 여러 객체를 참조하는 관계입니다. 예를 들어, 하나의 게시판에 여러 게시물이 있을 수 있습니다.
  • @OneToOne: 하나의 객체가 다른 하나의 객체를 참조하는 관계입니다. 예를 들어, 하나의 사용자는 하나의 프로필 정보를 가질 수 있습니다.
  • @ManyToMany: 여러 객체가 여러 객체를 상호 참조하는 관계입니다. 예를 들어, 여러 학생이 여러 과목을 수강할 수 있습니다.

연관관계 매핑을 사용하는 이유 객체 지향 프로그래밍의 특성과 관계형 데이터베이스의 특성을 모두 활용하기 위해서입니다. 객체 지향 프로그래밍에서는 객체 간의 관계를 객체 참조로 표현하면 코드가 더욱 직관적이고 이해하기 쉬워지며, 데이터의 일관성을 유지하는 도움이 됩니다. 반면 관계형 데이터베이스에서는 외래 키를 사용해 테이블 간의 관계를 표현하면 데이터의 무결성을 보장할 있습니다. JPA 연관관계 매핑은 가지 방식을 모두 적절하게 활용하도록 돕습니다.

 

5. JPA에서는 객체지향 프로그래밍과 관계형 데이터베이스의 패러다임 불일치 문제를 어떻게 해결하나요?

객체지향 프로그래밍과 관계형 데이터베이스는 각각의 패러다임이 있으므로, 이 두 세계를 연결하는 데는 몇 가지 문제가 발생합니다. 이를 "패러다임 불일치" 문제라고 부르며, JPA는 이러한 문제를 해결하기 위한 몇 가지 방법을 제공합니다.

  1. 상속: 객체지향 프로그래밍에서는 클래스 간에 상속 관계를 가질 수 있지만, 관계형 데이터베이스에서는 이러한 상속 관계를 표현하기 어렵습니다. JPA는 @Inheritance 어노테이션을 통해 객체의 상속 관계를 데이터베이스 스키마에 매핑할 수 있게 해줍니다.
  2. 연관관계: 객체지향 프로그래밍에서는 객체들 사이에 참조를 통한 연관관계를 가질 수 있습니다. 그러나 관계형 데이터베이스에서는 이러한 참조를 외래 키를 통해 연결해야 합니다. JPA는 이를 @OneToOne, @OneToMany, @ManyToOne, @ManyToMany 등의 어노테이션을 통해 객체 간의 참조 관계를 데이터베이스의 외래 키 관계에 매핑하도록 해줍니다.
  3. 객체 그래프 탐색: 객체지향 프로그래밍에서는 연관된 객체를 자유롭게 탐색할 수 있습니다. 즉, 객체 참조를 통해 연관된 객체를 가져올 수 있습니다. JPA는 이런 객체 그래프 탐색을 데이터베이스에 저장된 데이터에 대해서도 가능하게 해줍니다.
  4. 비교하기: 자바에서는 equals()와 hashCode() 메서드를 사용해 두 객체를 비교할 수 있습니다. JPA는 동일한 트랜잭션 내에서 같은 식별자를 가진 엔티티가 같은 것으로 취급되도록 도와줍니다.

이렇게 JPA 객체지향 프로그래밍과 관계형 데이터베이스 사이의 패러다임 불일치 문제를 해결함으로써, 개발자가 객체지향적인 코드를 작성하는 집중할 있도록 돕습니다.

 

6. JPA에서 성능 최적화를 위해 어떤 전략들을 사용할 수 있나요?

  1. Lazy Loading: Lazy Loading 연관된 엔티티를 필요할 때까지 로딩하지 않는 방법입니다. 이를 통해 필요하지 않은 데이터의 로딩을 피하고, 성능을 향상시킬 있습니다.
  2. Eager Loading: 반면에, 번의 쿼리로 여러 연관된 엔티티를 한꺼번에 로딩하는 Eager Loading 유용할 있습니다. 방식을 적절히 사용하면 여러 번의 쿼리를 줄이고, 성능을 향상시킬 있습니다.
  3. Second Level Cache(2차 캐시): JPA Second Level Cache 지원합니다. 이는 이미 로딩된 엔티티를 캐시에 저장하고, 같은 엔티티를 요청할 데이터베이스 대신 캐시에서 가져오는 기능입니다. 이를 통해 데이터베이스 액세스를 줄이고 성능을 향상시킬 있습니다.
  4. Batch Processing(배치처리): JPA 배치 처리를 지원합니다. 이는 번의 데이터베이스 트랜잭션에서 여러 엔티티의 변경을 한꺼번에 처리하는 방법입니다. 이를 통해 네트워크 비용을 줄이고, 성능을 향상시킬 있습니다.
  5. Fetch Join(페치조인): JPA JPQL에서 제공하는 Fetch Join 사용하면 관련된 엔티티를 번의 쿼리로 함께 조회할 있습니다. 이를 통해 쿼리의 수를 줄일 있습니다.
  6. N+1 문제 해결: N+1 문제는 쿼리를 너무 많이 실행하는 것을 말합니다. 문제는 Fetch Join이나 Batch Size 설정 등을 통해 해결할 있습니다.
  7. SQL 최적화: JPA JPQL 사용해 SQL 추상화하지만, 때로는 성능 최적화를 위해 네이티브 SQL 직접 사용해야 때가 있습니다.
  8. 엔티티 최적화: 불필요한 연관관계, 양방향 연관관계 등을 최적화하여 성능을 향상시킬 있습니다.
더보기
  1. Second Level Cache(2 캐시): JPA 데이터를 번째 저장 공간인 2 캐시에 보관하는 기능을 제공합니다. 여기에 보관된 데이터는 로딩되면 다시 사용할 있으므로, 같은 데이터를 다시 요청하더라도 데이터베이스에 직접 접근할 필요가 없습니다. 이를 통해 데이터베이스의 부하를 줄이고 성능을 향상시킬 있습니다. 그러나 기능을 사용할 때는 캐시된 데이터가 최신 상태를 유지하도록 주의해야 합니다.
    2차 캐시는 주로 변동이 적고 자주 조회되는 데이터에 적합합니다. 예를 들어, 국가 코드나 지역 코드와 같은 데이터는 변동이 거의 없으며, 많은 곳에서 참조하므로 2차 캐시에 저장하는 것이 효과적입니다. @Cacheable // 캐싱 활성화
  2. Batch Processing(배치 처리): JPA 여러 데이터를 번에 처리하는 배치 처리 기능을 지원합니다. 예를 들어, 100개의 데이터를 변경하려면 일반적으로 100번의 작업이 필요합니다. 그러나 배치 처리를 사용하면 100개의 작업을 번에 처리할 있습니다. 이렇게 하면 데이터베이스에 보내는 요청 횟수를 줄일 있으므로 성능을 향상시킬 있습니다.
    다량의 데이터를 한 번에 처리할 때 사용합니다
    for (int i = 0; i < size; i++) {
    entityManager.persist(entities.get(i));
    if (i % 50 == 0) { // 50개마다 flush와 clear를 호출
    entityManager.flush();
    entityManager.clear();

  3. Fetch Join(페치 조인): JPA에서는 페치 조인이라는 기능을 통해 관련된 여러 데이터를 번에 조회할 있습니다. 예를 들어, 사용자와 그들이 작성한 게시글을 모두 조회하려면 일반적으로 번의 조회가 필요합니다. 그러나 페치 조인을 사용하면 사용자와 게시글을 번의 조회로 함께 가져올 있습니다. 이렇게 하면 데이터베이스에 보내는 쿼리 수를 줄일 있습니다.
    연관된 엔티티를 한 번의 쿼리로 함께 조회하고 싶을 때 사용합니다
    String jpql = "SELECT p FROM Post p JOIN FETCH p.comments WHERE p.id = :id";
    TypedQuery<Post> query = em.createQuery(jpql, Post.class);
    query.setParameter(
    "id", postId);
    Post post = query.getSingleResult();
    Post 엔티티와 그에 연관된 Comment 엔티티를 한 번의 쿼리로 함께 조회하고 있습니다. 이렇게 하면 Post 엔티티를 조회할 때마다 Comment 엔티티를 별도로 조회하는 쿼리를 발생시키는 것을 방지할 수 있습니다.

 

2023.05.23 - [Spring] - JPA(Java Persistence API), ORM(Object-Relational Mapping) pt.1

2023.06.07 - [Mockterview] - JPA 지연 로딩(Lazy Loading)과 즉시 로딩(Eager Loading), N+1