1. 데이터베이스에서 인덱스는 데이터 검색 속도를 향상시키는 자료구조입니다. 인덱스는 일반적으로 B-Tree (바이너리 트리의 확장형) 같은 자료구조를 사용하여 데이터를 저장하며, 이를 통해 특정 데이터를 빠르게 검색할 있습니다.
  2. 인덱스의 원리를 책의 색인에 비유해 설명할 있습니다. 책의 내용을 찾고 싶을 , 모든 페이지를 일일이 훑어보는 것은 매우 비효율적입니다. 대신 색인을 확인하면 해당 키워드가 어느 페이지에 있는지 쉽게 찾을 있습니다.
  3. 데이터베이스에서도 이와 비슷하게 작동합니다. 인덱스를 사용하지 않는 테이블에서 데이터를 찾으려면 전체 테이블을 스캔해야 하지만, 인덱스를 사용하면 특정 데이터를 빠르게 찾을 있습니다.
  4. 그러나 인덱스는 무조건적으로 좋은 것만은 아닙니다. 인덱스를 생성하면 디스크 공간을 추가로 사용하며, 데이터를 삽입하거나 업데이트할 인덱스도 함께 업데이트해야 하므로 쓰기 작업이 느려질 있습니다. 따라서, 어떤 칼럼에 인덱스를 생성할지는 신중하게 결정해야 합니다.
  5. 일반적으로 데이터 검색이 자주 발생하고, 데이터의 중복도가 낮은 칼럼에 인덱스를 생성하는 것이 좋습니다. 이렇게 하면 인덱스를 통해 데이터를 빠르게 검색할 있고, 동시에 쓰기 성능 저하를 최소화할 있습니다.

**B-Tree는 Balanced Tree의 약자로, 모든 노드가 정해진 범위의 자식을 가지는 트리 구조입니다. 이러한 구조는 데이터베이스 시스템과 파일 시스템에서 인덱스를 구현하는 데 자주 사용됩니다.

B-Tree와 Binary Tree (이진 트리)는 이름이 비슷하여 혼동하기 쉽지만, 두 구조는 상당히 다릅니다.

Binary Tree에서 각 노드는 최대 두 개의 자식 노드를 가질 수 있습니다. 반면에 B-Tree에서 노드는 두 개 이상의 자식 노드를 가질 수 있습니다.

B-Tree는 균형이 잘 잡혀 있기 때문에, 데이터베이스에서 큰 양의 데이터를 빠르게 검색할 수 있습니다. B-Tree는 트리의 깊이를 최소화하므로, 특정 데이터를 찾기 위해 필요한 디스크 접근 횟수를 줄일 수 있습니다.

또한, B-Tree는 데이터 삽입과 삭제에 대한 오버헤드가 적습니다. B-Tree는 삽입과 삭제 시에도 트리의 균형을 유지하기 때문에, 인덱스의 성능을 일정하게 유지할 수 있습니다.

이러한 이유로, 대부분의 데이터베이스 시스템은 인덱스를 구현할 B-Tree 사용합니다.

더보기

B-Tree라는 개념을 가장 단순하게 이해하려면, 이를 "다중 분기"가 가능한 트리라고 생각하면 좋습니다. 일반적인 이진 트리(Binary Tree)는 각 노드가 최대 두 개의 자식 노드를 가지지만, B-Tree는 각 노드가 두 개 이상의 자식 노드를 가질 수 있습니다.

이를 평범한 책으로 비유해보겠습니다. 책의 목차를 상상해보세요. 첫 번째 장, 두 번째 장, 세 번째 장 등등으로 갈 수 있죠. 이를 이진 트리로 생각하면, 책의 각 장이 하나의 노드이고, 각 장이 두 개의 '자식' 섹션을 가지는 것이죠.

그런데 우리가 읽는 대부분의 책의 장은 보통 두 개 이상의 섹션을 가지고 있습니다. 이것이 바로 B-Tree의 개념입니다. 각 '노드' (또는 책의 장)는 두 개 이상의 '자식'을 가질 수 있습니다. 이런 식으로 B-Tree는 데이터를 훨씬 더 효과적으로 조직화하고 관리할 수 있습니다.

이와 같이 B-Tree 이진 트리를 확장한 형태로, 데이터베이스와 같이 대량의 데이터를 효율적으로 관리하고 검색할 있게 해줍니다.

 

인덱스란 데이터베이스 테이블에 대한 검색 성능의 속도를 높여주는 자료 구조입니다. 데이터 블록이 10만개 있다고 가정할 때 SELECT문 실행시 server process가 구문 분석 과정을 마친 후 database buffer cache 에 조건이 부합하는 데이터가 있는지 확인, 해당 정보가 buffer cache에 없다면 디스크 파일에서 조건에 부합하는 블럭을 찾아서 database buffer cache에 가져온 뒤 사용자에게 보여줌, 이 때 인덱스가 없으면 10만개를 전부 database buffer cache 로 복사한 후 풀스캔으로 찾게 되는데 index가 있으면 where절의 조건의 컬럼이 index의 키로 생성되어있는지 확인한 뒤, 인덱스에 먼저 가서 조건에 부합하는 정보가 어떤 ROWID를 가지고 있는지 확인 후 ROWID에 있는 블럭을 찾아가 해당 블럭만 buffer cache에 복사합니다.

** 데이터베이스 버퍼 캐시(Database Buffer Cache) 메모리 내의 공간으로, 데이터베이스에서 가장 자주 접근하는 데이터와 인덱스를 저장합니다. 이로 인해 디스크 I/O 작업이 줄어들며, 데이터베이스의 성능이 향상됩니다. 쿼리 실행 , 먼저 데이터베이스 버퍼 캐시에서 필요한 데이터를 찾고, 해당 데이터가 캐시에 없는 경우에만 디스크에서 데이터를 읽어오게 됩니다.

 

** ROWID 데이터베이스 테이블의 행을 고유하게 식별하는 값입니다. 이는 일반적으로 시스템에서 자동으로 생성되며, 사용자가 직접 변경할 없습니다. ROWID 특정 행의 물리적 위치를 나타내므로, 인덱스를 통해 ROWID 얻은 해당 ROWID 사용하여 데이터를 빠르게 접근할 있습니다

더보기

인덱스를 사용하면 데이터베이스는 WHERE 절의 조건이 일치하는 레코드를 찾기 위해 전체 테이블을 스캔하지 않고, 인덱스를 통해 레코드의 위치를 빠르게 찾아낼 수 있습니다. 이렇게 하면 쿼리의 성능을 크게 향상시킬 수 있습니다.

그러나 인덱스는 테이블에 쓰기 작업을 수행할 때 오버헤드를 발생시킵니다. 레코드를 삽입하거나 업데이트할 때마다, 데이터베이스는 해당 인덱스도 함께 업데이트해야 합니다. 따라서 인덱스는 검색 성능이 중요한 테이블에 주로 사용되며, 쓰기 작업이 빈번한 테이블에는 신중하게 사용해야 합니다.

또한 인덱스는 디스크 공간을 사용합니다. 인덱스를 생성할 때마다, 데이터베이스는 인덱스에 필요한 디스크 공간을 할당합니다. 이는 데이터베이스의 용량을 증가시키므로, 인덱스를 사용할지 여부는 검색 성능, 쓰기 성능, 그리고 디스크 공간 사용량 여러 요소를 고려하여 결정해야 합니다.

 

** B-Tree 인덱스 : B-Tree 인덱스는 가장 일반적으로 사용되는 인덱스 유형입니다. B-Tree 인덱스는 모든 값을 저장하며, 값은 해당 데이터를 가리킵니다. B-Tree 속성 값의 대소비교를 통해 찾고자 하는 값에 빠르게 접근할 있습니다. 대부분의 DBMS에서 기본 인덱스로 사용되며, 숫자 문자열 데이터에 효과적입니다.

B-Tree 인덱스는 균형 트리(Balanced Tree) 형태의 인덱스로, 데이터베이스에서 가장 일반적으로 사용되는 인덱스 유형입니다. B-Tree 인덱스의 각 노드는 키 값들을 정렬된 상태로 가지며, 이 키 값들을 통해 트리의 하위 노드를 찾을 수 있습니다. 따라서 특정 키 값을 찾거나 키 값의 범위를 검색하는 데에 효과적입니다.

예를 들어, B-Tree 인덱스를 사용해 학생 이름에 따른 성적 정보를 찾는다고 가정해봅시다. 학생 이름이 'Aaron' 학생의 성적을 찾기 위해 인덱스를 검색하면, B-Tree 인덱스는 'Aaron'이라는 이름을 가진 레코드를 빠르게 찾아줍니다. 또한 'Aaron'에서 'Brian'까지의 모든 학생들의 성적을 찾기 위해서도 B-Tree 인덱스를 사용할 있습니다.

 

** Hash 인덱스 : Hash 인덱스는 키를 해시 함수를 통해 변환하고, 해시 값을 통해 레코드에 접근하는 인덱스 유형입니다. 해시 인덱스는 특정 값을 통한 직접 검색에 매우 빠르지만, 값의 범위를 통한 검색이나 값의 부분 일치로 검색하는 것은 비효율적입니다. 따라서, 해시 인덱스는 특정 키로 레코드를 직접 검색하는 경우에만 효과적입니다.

해시 인덱스는 키를 해시 함수를 통해 해시 값으로 변환하고, 이 해시 값을 통해 레코드를 찾는 인덱스입니다. 해시 인덱스는 특정 키 값을 통한 직접 검색에 매우 빠르지만, 키 값의 범위를 통한 검색이나 키 값의 부분 일치로 검색하는 것은 불가능합니다.

예를 들어, 해시 인덱스를 사용해 주문 번호에 따른 주문 정보를 찾는다고 가정해봅시다. 주문 번호가 '12345' 주문의 정보를 찾기 위해 인덱스를 검색하면, 해시 인덱스는 '12345'라는 주문 번호를 가진 레코드를 빠르게 찾아줍니다. 그러나 '12340'에서 '12350'까지의 모든 주문들의 정보를 찾기 위해서는 해시 인덱스를 사용할 없습니다. 해시 인덱스는 특정 키로 레코드를 직접 검색하는 경우에만 효과적입니다.

 

 B-Tree 인덱스는 키의 범위 검색이 필요한 경우나, 값의 부분 일치로 검색하는 경우에 유용하고, 해시 인덱스는 특정 키로 레코드를 빠르게 검색해야 하는 경우에 유용합니다.

 

더보기

Bitmap 인덱스 : Bitmap 인덱스는 특정 속성의 값이 매우 작은 경우에 사용됩니다. 이런 경우, 속성 값에 대한 비트 배열을 만들어 행이 해당 값을 가지고 있는지 아닌지를 표시합니다. Bitmap 인덱스는 간단하고 공간 효율적이며, 데이터의 특정 조합을 검색할 빠르다는 장점이 있습니다. 하지만 데이터 수정에는 비효율적이며, 일반적으로 속성의 유일한 값이 적고, 데이터가 자주 변경되지 않는 경우에만 사용됩니다.

Multi-column 인덱스(복합 인덱스), Full-text 인덱스(전문 검색 인덱스), Spatial 인덱스(공간 인덱스) 등 다양한 인덱스 유형이 있습니다. 

 

2023.05.24 - [Mockterview] - 인덱스(Index) pt.2

'Mockterview' 카테고리의 다른 글

트리(tree)와 그래프(graph)  (0) 2023.05.23
이분탐색(Binary Search)의 시간복잡도 = O(log n)  (0) 2023.05.23
Annotation(@)  (0) 2023.05.19
더티체킹 (Dirty Checking)  (0) 2023.05.19
JPA(Java Persistence API)  (0) 2023.05.19

Annotation은 메타데이터를 코드에 추가하는 방법 중 하나로, Java, C#, Python 등 많은 프로그래밍 언어에서 지원합니다. 어노테이션은 코드의 동작에 직접적인 영향을 미치지 않지만, 해당 코드에 대한 정보를 제공하거나 특정 동작을 유도하는 데 사용됩니다.

자바에서 어노테이션은 '@' 기호로 시작하며, 클래스, 메소드, 필드, 매개변수 등 다양한 요소에 적용할 수 있습니다. 여러 어노테이션 중 몇 가지 예시를 들면:

  1. @Override: 이 어노테이션은 메소드가 상위 클래스나 인터페이스의 메소드를 오버라이드한다는 것을 나타냅니다. 만약 실제로 오버라이드하는 메소드가 아닌데 @Override 어노테이션을 사용하면 컴파일 에러가 발생합니다.
  2. @Deprecated: 이 어노테이션은 해당 요소(클래스, 메소드 등)가 더 이상 사용되지 않을 것을 권장하며, 다른 방법을 사용하도록 권고하는 의미입니다.
  3. JPA에서의 어노테이션: JPA에서는 엔티티 클래스와 그 필드에 다양한 어노테이션을 사용하여 데이터베이스 스키마와의 매핑 정보를 제공합니다. 예를 들어, @Entity는 해당 클래스가 엔티티 클래스임을 나타내고, @Table은 엔티티가 매핑될 테이블의 이름을 지정합니다. @Id는 해당 필드가 엔티티의 기본 키임을 나타내며, @GeneratedValue는 기본 키의 생성 전략을 지정합니다.

** 어노테이션(Annotation): 어노테이션은 메타데이터를 코드에 추가하는 사용되며, 메타데이터는 컴파일 타임이나 런타임에 처리될 있습니다. 어노테이션은 코드의 동작에 직접적인 영향을 미치지 않지만, 컴파일러, 개발 도구, 런타임 환경 등에게 추가 정보를 제공하거나 특정 동작을 유도하는 사용됩니다. 예를 들어, JPA에서 @Entity 어노테이션은 해당 클래스가 엔티티임을 나타내며, 정보는 JPA 프레임워크에 의해 런타임에 사용됩니다.

 

어노테이션은 일반적으로 다음 세 가지 범주로 분류될 수 있습니다.

  1. Built-in Annotation: 자바에서 기본으로 제공하는 어노테이션입니다. @Override, @Deprecated, @SuppressWarnings 등이 이에 해당합니다.
  2. Meta Annotation: 다른 어노테이션을 위한 어노테이션으로, 주로 어노테이션의 선언과 특성을 정의하는 데 사용됩니다. @Target, @Retention, @Documented, @Inherited 등이 있습니다.
  3. Custom Annotation: 개발자가 직접 정의하여 사용하는 어노테이션입니다. 이를 통해 특정 동작을 유도하거나, 특정 정보를 코드에 첨부할 수 있습니다. 대표적으로 Spring 프레임워크에서는 다양한 사용자 정의 어노테이션(@Controller, @Service, @Autowired 등)을 제공하며, 이는 특정 클래스나 메소드가 프레임워크 내에서 어떤 역할을 하는지를 나타냅니다.

가지 종류의 어노테이션은 자바 프로그래밍에서 코드의 동작을 제어하고, 메타데이터를 첨부하는 유용하게 사용됩니다.

 

** 롬복(Lombok)은 개발자가 직접 만든 라이브러리로, 간단한 어노테이션을 사용해 반복적으로 작성해야 하는 보일러플레이트 코드를 줄이는 데 도움을 줍니다. 따라서 롬복의 어노테이션은 Custom Annotation에 속합니다.

롬복의 어노테이션들은 클래스, 필드, 메소드 등에 대한 getter, setter, 생성자, toString, equals, hashCode 등의 메소드를 자동으로 생성해줍니다. 예를 들어, @Data 어노테이션은 위 메소드들을 모두 자동으로 생성해줍니다.

  1. 보일러플레이트 코드 개발에 있어서 반복적으로 사용되는 코드를 일컫습니다. 이러한 코드는 반드시 필요하지만 구조적인 부분 때문에 반복적으로 작성되어야 하는 코드이며, 이를 최소화하는 것이 유지보수와 가독성 측면에서 좋습니다.
  2. 롬복(Lombok) 이러한 보일러플레이트 코드를 줄이기 위한 라이브러리입니다

이는 개발자가 이러한 메소드를 수동으로 생성하고 유지 관리하는 수고를 덜어주며, 코드의 가독성을 향상시킵니다. 물론, 롬복을 사용함으로써 발생할 있는 가지 단점도 있습니다.

  1. 디버깅 복잡성: 롬복이 자동으로 생성하는 코드는 실제 소스 코드에는 보이지 않습니다. 이는 디버깅 문제를 찾기 어렵게 만들 있습니다.
  2. 코드의 명시성 감소: 롬복 어노테이션을 사용하면 중요한 메소드가 소스 코드에 나타나지 않게 됩니다. 이로 인해 코드의 이해가 어려워질 있습니다. 
더보기
  1. @Component: 어노테이션은 클래스를 스프링 빈으로 등록하는데 사용됩니다. 스프링이 시작될 , @Component 어노테이션이 있는 클래스를 찾아서 인스턴스를 생성하고, 이를 스프링 애플리케이션 컨텍스트의 빈으로 등록합니다.
  2. @Bean: 어노테이션은 메소드에 적용되며, 메소드가 반환하는 객체를 스프링 빈으로 등록하는데 사용됩니다. @Bean 어노테이션이 주로 사용되는 경우는 개발자가 직접 제어할 없는 외부 라이브러리나, 사용자 정의 생성 로직이 필요한 경우입니다.
  3. @Controller: 어노테이션은 해당 클래스가 요청을 처리하는 컨트롤러의 역할을 수행함을 스프링에 알려줍니다. 일반적으로 @Controller 어노테이션이 있는 클래스는 요청과 관련된 메소드를 포함합니다.
  4. @RequestMapping: 어노테이션은 특정 URI 클래스나 메소드에 매핑되어야 함을 나타냅니다. @RequestMapping 어노테이션이 있는 메소드는 해당 URI 요청이 들어올 호출됩니다.

 

Annotiation은 클래스와 메서드에 추가하여 다양한 기능을 부여하는 역할을 합니다. Annotation을 이용하여 Spring Framwork는 해당 클래스가 어떤 역할을 할지 정하고, 특별한 의미를 부여하거나 기능을 부여할 수 있고, 이를 통해 코드량이 줄고 유지보수가 쉬워지고 코드 생산성이 증가되는 장점이 있습니다. @Component는 개발자가 생성한 Class를 Spring Bean으로 등록할때 사용하는 Annotation입니다. Spring은 해당 Annotation을 보고 Spring의 Bean으로 등록합니다. @Bean 은 개발자가 제어가 불가능한 외부 라이브러리와 같은 것들을 Bean으로 만들 때 사용합니다. @Controller는 Spring에게 해당 class가 Controller의 역할을 함을 명시하기 위해 사용합니다. @RequestMapping은 요청 들어온 URI의 요청과 Annotation value 값이 일치하면 해당 클래스나 메소드를 실행합니다.

 

더보기

어노테이션과 주석은 서로 다른 목적과 사용 방법을 가지며, 코드에 대한 부가적인 정보를 제공하는 방식도 다릅니다. 주석은 코드를 이해하는 데 도움이 되는 설명을 제공하며, 어노테이션은 코드의 동작을 제어하거나 추가 정보를 제공하는 데 사용됩니다.

 

주석(Comment): 주석은 코드의 동작에 전혀 영향을 미치지 않습니다. 주석은 코드를 읽는 사람(대부분은 개발자)에게 코드의 작동 방식, 목적, 관련된 문제 등에 대한 정보를 전달하기 위해 사용됩니다. 주석은 컴파일러나 인터프리터에 의해 무시되며, 실행 중인 프로그램에는 영향을 미치지 않습니다.

 

* 메타데이터(MetaData) : 

데이터에 대한 데이터라는 의미를 가집니다. 즉, 다른 데이터를 설명해주는 정보를 말합니다.

메타데이터는 다양한 용도로 사용될 수 있습니다. 예를 들어, 웹 페이지의 메타데이터는 해당 페이지의 내용, 작성자, 작성일, 태그 등의 정보를 포함할 수 있으며, 이는 검색 엔진이 웹 페이지를 인덱싱하는 데 도움이 됩니다.

또 다른 예로, 디지털 사진의 메타데이터는 사진의 해상도, 촬영 일시, 카메라 모델, 사용한 렌즈, ISO 속도, 셔터 속도 등의 정보를 포함할 수 있습니다.

데이터베이스에서의 메타데이터는 테이블의 이름, 테이블에 있는 행과 열의 수, 각 열의 데이터 타입, 인덱스 정보, 제약 조건 등을 포함하며, 이는 데이터베이스의 구조와 작동 방식을 이해하는데 필요합니다.

따라서 메타데이터는 데이터를 분류, 관리, 이해하고, 데이터가 어떤 것인지, 어떻게 생성되었는지, 어떻게 사용되어야 하는지에 대한 중요한 정보를 제공합니다.

JPA에서 제공하는 기능 하나로, 트랜잭션 내에서 객체의 상태 변화를 자동으로 감지하고, 변화가 있을 경우 데이터베이스에 해당 변경 사항을 자동으로 반영하는 기능입니다. 이를 통해 개발자는 데이터베이스와의 상호작용에 대해 더욱 객체 지향적으로 접근할 있게 됩니다.

  1. 상태 변경 확인: JPA는 트랜잭션 내에서 관리되는(영속 상태인) 엔티티의 초기 상태를 저장해두고, 트랜잭션이 끝날 때 이 초기 상태와 비교합니다. 이렇게 해서 엔티티의 상태가 트랜잭션 내에서 변경되었는지를 확인하게 됩니다. 이런 확인 과정을 '더티 체킹'이라고 합니다.
  2. 변경 반영: 만약 엔티티의 상태가 변경되었다면 (즉, '더티'하다면), JPA는 이 변경을 데이터베이스에 반영합니다. 이를 위해 JPA는 적절한 SQL UPDATE 쿼리를 생성하고 실행합니다. 이렇게 해서 데이터베이스의 상태도 엔티티의 변경된 상태와 일치하게 됩니다.

즉, 더티 체킹은 JPA 엔티티의 변경을 자동으로 관리해주는 중요한 기능입니다. 덕분에 개발자는 SQL UPDATE 쿼리를 직접 작성하거나 실행할 필요 없이, 단순히 자바 코드를 통해 엔티티의 상태를 변경하기만 하면 됩니다.

 

더티 체킹이란 “상태 변경 검사”입니다. JPA에서는 트랜잭션이 끝나는 시점에 최초 조회 상태(변경기준)로부터 변화가 있는 모든 엔티티 객체를 데이터베이스에 자동으로 반영해줍니다. 엔티티를 조회하면 해당 엔티티의 조회 상태 그대로 스냅샷을 만들어 놓고 트랜잭션이 끝나는 시점에는 이 스냅샷과 비교해서 다른점이 있다면 Update Query를 데이터베이스로 전달합니다. 상태 변경 검사의 대상은 영속성 컨텍스트가 관리하는 엔티티에만 적용됩니다. 즉, 준영속/비영속 상태의 엔티티는 더티 체킹의 대상에 포함되지 않아 값을 변경해도 데이터베이스에 반영되지 않습니다

 

더티 체킹이란 상태 변화 검사를 의미합니다. JPA는 트랜잭션이 끝나는 시점에 변화가 있는 모든 엔티티 객체를 데이터베이스에 자동으로 반영해줍니다. 엔티티를 조회할 때 스냅샷을 만들고 트랜잭션이 끝나는 시점에 스냅샷과 비교를 하여 달라진 부분은 Update 합니다. 영속성 컨텍스트에 관리되는 엔티티만 검사 대상이며 이외 엔티티는 검사 대상이 아닙니다.

 

비영속: JPA와 전혀 관계없이 객체만 생성한 상태

준영속(detached): 영속성 컨텍스트에 저장되었다가 분리된 상태로 현재는 영속 상태가 아닌 상태

 

2023.05.23 - [Spring] - 영속성 컨텍스트(Persistence Context), 1차 캐시

 

'Mockterview' 카테고리의 다른 글

Index(인덱스), B-tree, Hash pt.1  (0) 2023.05.19
Annotation(@)  (0) 2023.05.19
JPA(Java Persistence API)  (0) 2023.05.19
MVC (Model View Controller)패턴  (0) 2023.05.19
Override(오버라이딩) 와 Overload(오버로딩)  (0) 2023.05.19

Java에서 제공하는 ORM(Object-Relational Mapping) 표준 스펙.ORM 객체와 관계형 데이터베이스의 테이블 간에 매핑을 제공하여 (불일치 문제를 해결하여) 개발자가 SQL 쿼리를 작성하지 않고도 데이터베이스 작업을 수행할 있게 합니다.

 

JPA의 주요 이점은 다음과 같습니다:

  1. 객체지향적인 코드 : 개발자는 데이터베이스 테이블과 직접적인 상호 작용을 거치지 않고, 대신 자바 객체를 다루는 방식으로 데이터베이스 작업을 수행할 수 있습니다. 이로 인해 코드가 더욱 간결하고 명확해지며, 객체지향 프로그래밍의 이점을 최대한 활용할 수 있습니다.
  2. 데이터베이스 독립성 : JPA는 다양한 데이터베이스 시스템과 호환됩니다. 따라서 SQL 쿼리를 직접 작성할 필요 없이 동일한 자바 코드로 다른 데이터베이스 시스템에 연결할 수 있습니다.
  3. 생산성 향상 : CRUD(Create, Read, Update, Delete) 작업과 같은 일반적인 작업에 대해 JPA는 자동화된 메서드를 제공합니다. 이를 통해 개발 시간을 단축하고 코드의 재사용성을 향상시킬 수 있습니다.
더보기

JPA가 필요한 경우 :

  1. 객체-관계 매핑 (ORM) 필요한 경우: ORM 객체지향 프로그래밍 언어와 데이터베이스 간의 '불일치' 문제를 해결하는 기술입니다. JPA 이러한 ORM 기능을 제공합니다. 따라서 객체 지향적인 코드를 작성하면서 데이터베이스와의 상호작용을 원활하게 필요가 있다면 JPA 사용해야 합니다.
  2. 데이터베이스 독립성이 필요한 경우: JPA 데이터베이스에 독립적입니다. , 특정 데이터베이스에 종속되지 않고, 다양한 데이터베이스 시스템과 호환됩니다. 이런 이유로 여러 다른 데이터베이스를 사용하거나, 향후 데이터베이스 변경 가능성이 있는 경우에도 JPA 사용하는 것이 좋습니다.
  3. CRUD 작업 간소화: JPA 사용하면 개발자는 데이터베이스에 대한 CRUD(Create, Read, Update, Delete) 연산을 쉽게 수행할 있습니다. 따라서 데이터베이스에 대한 기본적인 작업을 간소화하려면 JPA 유용할 있습니다.

그러나 JPA는 모든 상황에 적합한 것은 아닙니다. 주요 제약사항은 다음과 같습니다:

  1. 복잡한 쿼리 : 복잡한 쿼리나 최적화된 쿼리를 작성해야 하는 경우에는 SQL 직접 사용하는 것이 효율적일 있습니다. JPA 일반적인 쿼리에 적합하지만, 복잡한 쿼리를 만드는데 있어 제한적입니다.
  2. 성능 이슈 : JPA 객체-테이블 매핑을 통해 편리함을 제공하지만, 과정에서 생기는 오버헤드가 성능을 저하시킬 있습니다. 따라서 대규모 데이터를 다루는 상황에서는 성능 문제가 발생할 있습니다.
더보기

JPA가 필요하지 않은 경우 : 

  1. 단순한 애플리케이션 또는 프로토타입 개발: 작은 규모의 애플리케이션 또는 프로토타입을 개발하는 경우, JPA 오버헤드를 초래할 있습니다. 이런 경우, 단순 SQL 쿼리 또는 가벼운 데이터 접근 기술을 사용하는 것이 적합할 있습니다.
  2. 복잡한 SQL 쿼리 필요: JPA ORM 사용하여 복잡한 SQL 쿼리를 처리하는데 제한이 있을 있습니다. 만약 복잡한 쿼리와 작업을 수행해야 한다면, JDBC 같은 다른 기술을 사용하는 것이 나을 있습니다.
  3. 고성능이 필요한 경우: JPA 강력한 기능이지만, 때때로 성능이 중요한 애플리케이션에서는 JPA 사용하는 것이 성능을 저하시킬 있습니다. ORM 초래하는 오버헤드, 지연 로딩과 같은 특성 때문에 매우 빠른 응답 시간이 요구되는 상황에서는 JPA보다는 저수준의 데이터 접근 기술이 적합할 있습니다.
  4. 프로시저가 많은 레거시 데이터베이스를 사용하는 경우: 데이터베이스에 프로시저가 많은 경우 JPA보다는 JDBC 사용하는 것이 효과적일 있습니다. JPA 프로시저를 지원하지만, 복잡한 프로시저를 다루는 데에는 JDBC 유연성을 제공합니다.(즉, JPA 같은 ORM 기술보다는 직접 SQL 다루는 JDBC 같은 기술이 효율적일 있습니다. 이는 JPA 복잡한 프로시저를 다루는 데에는 한계가 있기 때문입니다.)
  5. 비관계형 데이터베이스 사용: JPA 관계형 데이터베이스에 최적화되어 있습니다. NoSQL 등의 비관계형 데이터베이스를 사용하는 경우, 해당 데이터베이스에 특화된 기술을 사용하는 것이 적합할 있습니다.

**오버헤드(overhead)란 컴퓨터 과학에서 일반적으로 어떤 처리를 하기 위해 들어가는 직접적이지 않은 비용을 의미합니다. 예를 들어, JPA 같은 ORM 프레임워크를 사용하면 개발자는 SQL 쿼리를 직접 작성하는 것보다 높은 수준에서 데이터베이스와 상호 작용할 있지만, 이는 추가적인 처리 시간이나 메모리 사용으로 이어질 있습니다. 이런 추가적인 비용이 바로 "오버헤드"입니다.

 

**프로토타입(prototype)이란 어떤 제품이나 소프트웨어의 초기 버전을 의미합니다. 이는 최종 제품이나 시스템의 기능, 디자인, 성능 등을 테스트하고 검증하기 위한 모델입니다. 프로토타입 개발에서는 일반적으로 간결하고 빠른 개발이 중요하며, 때때로 전체 기능이나 최적화는 뒤로 미루어질 있습니다. JPA 같은 복잡한 프레임워크를 사용하는 것이 프로토타입 개발에 있어서는 과도한 오버헤드를 초래할 있으며, 이럴 때는 보다 간단한 데이터 접근 기술을 사용하는 것이 적합할 있습니다.

 

**프로시저(procedure) SQL 사용하여 작성되며, 데이터베이스 내에 저장되어 재사용이 가능한 일련의 SQL 문을 의미합니다. 프로시저는 로직을 데이터베이스 서버에 저장함으로써 애플리케이션과 데이터베이스 사이의 네트워크 트래픽을 줄이고, 코드의 재사용성을 높이며, 보안을 강화하는 등의 이점이 있습니다.

**ORM은 "Object-Relational Mapping"의 약자로, 객체-관계 매핑을 의미합니다. 이는 객체 지향 프로그래밍 언어와 관계형 데이터베이스 사이의 '불일치' 문제를 해결하는 기술입니다.

객체 지향 프로그래밍 언어에서는 데이터를 객체(object)라는 형태로 다룹니다. 객체는 상태(state)와 행동(behavior)을 가지며, 이들은 각각 필드(field)와 메서드(method)로 표현됩니다. 또한 객체 지향 프로그래밍에서는 객체들 간의 관계를 표현하기 위해 상속, 다형성, 캡슐화 등의 개념을 사용합니다.

반면, 관계형 데이터베이스에서는 데이터를 테이블(table) 형태로 저장하며, 이들 테이블은 행(row)과 열(column)로 구성됩니다. 테이블 간의 관계는 주로 외래키(foreign key)를 사용해 표현됩니다.

이렇게 객체 지향 프로그래밍과 관계형 데이터베이스는 데이터를 다루는 방식이 매우 다르기 때문에, 이 둘 사이의 '불일치' 문제가 발생할 수 있습니다. 이를 해결하기 위해 ORM이라는 기술이 사용됩니다.

ORM은 객체 지향 프로그래밍 언어에서 사용하는 객체를 관계형 데이터베이스의 테이블에 자동으로 매핑해줍니다. 즉, 개발자는 SQL 쿼리를 직접 작성하는 대신 객체 지향적인 코드를 작성하면, ORM이 이를 SQL 쿼리로 변환하여 데이터베이스와의 상호작용을 담당합니다. 이로써 개발자는 데이터베이스 처리를 위한 복잡한 코드 작성을 최소화하고, 코드의 가독성과 유지보수성을 향상시킬 수 있습니다.

Java Persistence API(JPA) ORM 구현한 기술 하나로, 자바 어플리케이션에서 관계형 데이터베이스의 데이터를 관리하는 표준 API입니다.

 

더보기
  1. JDBC(Java Database Connectivity): JDBC는 자바에서 데이터베이스에 접근할 수 있도록 하는 API입니다. 이를 사용하면 자바 어플리케이션에서 SQL 쿼리를 실행하고, 그 결과를 처리할 수 있습니다. JDBC는 SQL을 직접 사용하므로, 데이터베이스의 세부적인 특성을 제어할 수 있지만, 코드가 복잡해지는 단점이 있습니다. 또한 JDBC를 사용하면 데이터베이스 별로 다른 SQL을 사용해야 하는 문제도 있습니다.
  2. JPQL(Java Persistence Query Language): JPQL은 JPA(Java Persistence API)에서 사용하는 쿼리 언어입니다. 이는 SQL과 유사하지만, 데이터베이스 테이블이 아닌 자바의 엔티티 객체를 대상으로 쿼리를 수행합니다. 즉, 객체 지향적인 쿼리를 작성할 수 있으며, 이는 ORM(Object-Relational Mapping)의 일환입니다. 또한 JPQL은 데이터베이스에 독립적이므로, 다양한 데이터베이스에서 동일한 JPQL 쿼리를 사용할 수 있습니다. 그러나 복잡한 쿼리나 특정 데이터베이스의 고유 기능을 사용하려면 JPQL보다는 SQL이 적합할 수 있습니다.
  3. 따라서, JDBC와 JPQL은 각각 SQL과 JPQL이라는 서로 다른 쿼리 언어를 사용하며, 데이터베이스에 접근하고 쿼리를 실행하는 방식에 차이가 있습니다. JDBC는 저수준의 데이터베이스 접근을 제공하며, JPQL은 객체 지향적인 쿼리 작성을 가능하게 하는 고수준의 쿼리 언어입니다.
  4. JPA 는 어플리케이션과 JDBC 사이에서 동작하여 개발자는 JPA 에게 명령하고 JPA 가 JDBC API 를 사용해 SQL 을 실행한다.

 

데이터베이스가 바뀔 가능성이 있는 경우 JPA를 권장합니다. JPA는 추상화한 데이터 접근 계층을 제공하기 때문에 설정 파일에 사용할 데이터베이스를 등록하기만 하면 얼마든 데이터베이스를 변경할 수 있습니다. 그러나 통계 처리 같은 복잡한 쿼리가 필요한 경우에는 JPA 보다는 SQL문이 오히려 나을 수도 있습니다. JPA에서 Native SQL Query를 사용할 수 있지만 특정 데이터베이스에 종속 된다는 문제가 있습니다. 이를 보완하기 위해 JPA에서는 SQL과 유사한 기술인 JPQL을 지원합니다. 그리고 JPA에 대한 이해가 부족해 잘못된 매핑으로 설계하는 경우 N+1 문제로 성능 저하가 발생할 수 있습니다

 

JPA는 ORM을 위해 JAVA에서 제공하는 API이며 특정 DB에 종속되지 않습니다. 구현체로는 Hibernate가 있습니다. JPA를 사용하면 메소드 호출만으로 쿼리가 실행되니 생산성이 향상되고 유지 보수에 코스트가 하락합니다. 이로 인해 비즈니스 로직에 집중 가능하고 객체 중심 프로그래밍이 가능하게 되지만, 복잡한 쿼리문 구현이 힘들다는 단점이 있습니다. 결국 데이터베이스 설계 중심의 패러다임을 객체 설계 중심으로 역전 시킬 수 있는 JPA 사용은 긍정적이지만, 통계 쿼리처럼 매우 복잡한 SQL을 작성하기에는 적합하지 않습니다.

 

JPA는 java 진영의 표준 ORM으로 관계형DB와 객체지향 언어인 java 와의 관계에서 쿼리문 작성 없이 편하게 데이터 다룰 수 있게 해주는 도구입니다. 쿼리문을 직접 작성할 필요가 없어 생산성이 향상되고 오류의 여지가 적어지는 장점이 있지만 통계를 내는 등의 복잡한 쿼리의 작성이 필요한 경우 적절하지 않을수 있습니다. 일반적으로 많이 사용하지만 기술의 장단점을 인지하고 장점이 발휘될수 있는 환경에서 적절하게 사용하는게 좋습니다.

'Mockterview' 카테고리의 다른 글

Annotation(@)  (0) 2023.05.19
더티체킹 (Dirty Checking)  (0) 2023.05.19
MVC (Model View Controller)패턴  (0) 2023.05.19
Override(오버라이딩) 와 Overload(오버로딩)  (0) 2023.05.19
Call by reference  (0) 2023.05.18

SpringBoot는 프레임 워크이고 프레임 워크란 정해진 규칙이 있는것이다.

규칙 1. MVC 패턴을 사용한다

규칙2. 계층간 데이터를 이동할때 다른 계층을 사용하지 않는다. 

 

이때 MVC계층이란

1. Model : 핵심적인 비즈니스 로직(연산, 데이터 정제, 데이터베이스의 통신) 계층, 즉 View에 적용할 정보들

2. View : 화면을 보여주기 위한 계층(동적 HTML 파일)

3. Controller : 유저의 요청을 수신하는 계층(Client 의 요청을 Model 로 받아 처리)

 

MVC는 Model, View, Controller의 약자이며, 각 레이어 간 기능을 구분하는데 중점을 둔 개발 방법론 중 하나입니다. Model은 데이터 관리 및 비즈니스 로직을 처리하는 부분이며, View는 비즈니스 로직의 처리 결과를 통해 유저 인터페이스가 표현되는 구간입니다. Controller는 사용자의 요청을 처리하고 Model과 View를 중개하는 역할을 합니다. Model과 View는 서로 연결되어 있지 않기 때문에 Controller가 사이에서 통신 매체가 되어줍니다.( 데이터와 비즈니스 로직 사이의 상호작용을 관리)

  1. 모델(Model): 모델은 애플리케이션의 비즈니스 로직 및 데이터를 관리합니다. 이는 데이터베이스와의 상호 작용, 쿼리 수행, 데이터 조작 등을 수행하며, 애플리케이션의 '상태'를 나타냅니다.
  2. 뷰(View): 뷰는 사용자에게 보여지는 부분으로, 사용자 인터페이스(UI)와 데이터의 시각적 표현을 담당합니다. 일반적으로 HTML, CSS, JavaScript 등을 사용하여 웹 페이지를 생성하거나 업데이트합니다.
  3. 컨트롤러(Controller): 컨트롤러는 모델과 뷰 사이의 인터페이스로 작동하며, 사용자의 입력을 처리하고 적절한 응답을 생성하는 역할을 합니다. 사용자의 요청을 받아 이를 모델에 전달하여 상태를 변경하거나, 모델에서 데이터를 가져와 뷰를 업데이트합니다.

 

Controller 와 HTTP Response 메시지
Controller 와 HTTP Request 메시지

**스프링 MVC 동작원리

  1. Client → DispatcherServlet
    • 가장 앞 단에서 요청을 받아 FrontController 라고도 불림
  2. DispatcherServlet → Controller
    • API 를 처리해 줄 Controller 를 찾아 요청을 전달
    • Handler mapping 에는 API path 와 Controller 함수가 매칭되어 있음
  3. Controller → DispathcerServlet
    • Controller 가 Client 으로 받은 API 요청을 처리
    • 'Model' 정보와 'View' 정보를 DispatcherServlet 으로 전달
  4. DispatcherServlet → Client
    • ViewResolver 통해 View 에 Model 을 적용
    • View 를 Client 에게 응답으로 전달

** Template engine : View 에 Model 을 적용 → 동적 웹페이지 생성

  1. 예) 로그인 성공 시, "로그인된 사용자의 id" 를 페이지에 추가
  2. Template engine 종류: 타임리프 (Thymeleaf), Groovy, FreeMarker, Jade 등 (스프링에서 JSP 이용은 추천하지 않고 있음)

 

* MVC 패턴이란 무엇이며, 그 장점은 무엇인가요?

MVC(Model-View-Controller)는 소프트웨어 디자인 패턴 중 하나로, 특히 웹 개발에서 널리 사용됩니다. 이 패턴은 애플리케이션을 세 가지 역할로 분리합니다:

  1. 모델(Model): 모델은 데이터와 비즈니스 로직을 관리하는 컴포넌트입니다. 이는 데이터베이스와 상호작용하고, 애플리케이션의 상태를 유지합니다.
  2. 뷰(View): 뷰는 사용자에게 보여지는 부분으로 사용자 인터페이스를 관리합니다. 사용자가 볼 수 있는 데이터의 표현을 담당하며, 일반적으로 웹 페이지나 화면을 구성하는 역할을 합니다.
  3. 컨트롤러(Controller): 컨트롤러는 사용자의 입력을 받아 모델과 뷰에 전달하는 역할을 합니다. 사용자의 요청을 해석하고 그에 따라 모델의 상태를 변경하거나, 모델의 상태에 따라 뷰를 업데이트합니다.

MVC 패턴의 주요 장점은 다음과 같습니다:

  1. 모듈화와 재사용성: MVC 패턴은 코드의 모듈화를 촉진하며, 이로 인해 각 컴포넌트를 독립적으로 개발하고 테스트할 수 있습니다. 이는 코드의 재사용성을 증가시킵니다.
  2. 유지보수성: 각 컴포넌트가 독립적인 역할을 가지므로, 한 컴포넌트의 수정이 다른 컴포넌트에 미치는 영향을 최소화합니다. 이는 유지보수를 용이하게 합니다.
  3. 동시 개발 가능: 서로 다른 개발 팀이 모델, 뷰, 컨트롤러를 독립적으로 개발할 수 있으므로, 개발 시간을 단축시킬 수 있습니다.
  4. 높은 확장성: 새로운 기능이 필요할 때 모델, 뷰, 컨트롤러 중 필요한 컴포넌트만 추가하거나 변경하면 됩니다. 이는 애플리케이션의 확장성을 증가시킵니다.
  5. 유연성: MVC 패턴은 프레젠테이션 레이어와 비즈니스 로직 레이어를 분리함으로써 유연성을 제공합니다. 예를 들어, 사용자 인터페이스를 변경하고 싶을 때 뷰만 수정하면 되고, 데이터 처리 방식을 변경하고 싶을 때는 모델만 수정하면 됩니다.
  6. 효율적인 코드 관리: MVC 패턴은 애플리케이션의 코드 구조를 논리적으로 구성함으로써 코드의 관리를 용이하게 합니다. 코드의 논리적인 구성은 코드의 가독성과 이해를 쉽게 만듭니다.
  7. 다중 뷰 지원: 하나의 모델에 대해 여러 뷰를 가질 수 있습니다. 예를 들어, 웹 애플리케이션의 데이터를 다양한 방식으로 표시할 수 있습니다.
    더보기

    다중 뷰 지원은 MVC 디자인 패턴의 특징 중 하나로, 이는 한 모델에 여러 개의 뷰를 연결할 수 있음을 의미합니다. 즉, 동일한 데이터를 다른 방식으로 표시할 수 있다는 것입니다. 이것은 사용자 인터페이스의 유연성을 높이며, 사용자에게 다양한 시각적 경험을 제공할 수 있습니다.

    예를 들어, 온라인 쇼핑 웹 사이트를 생각해봅시다. 이 사이트의 제품 데이터는 모델에 저장되어 있을 것입니다. 하지만 이 데이터는 여러 다른 뷰를 통해 표현될 수 있습니다.

    • 리스트 뷰: 제품들이 한 줄로 나열된 형태로 보여집니다. 각 제품에 대한 간단한 정보만 표시될 수 있습니다.
    • 그리드 뷰: 제품들이 격자 형태로 표시됩니다. 이 뷰는 이미지 중심으로, 각 제품에 대한 자세한 정보를 보여줄 수 있습니다.
    • 상세 뷰: 한 제품에 대한 모든 정보를 표시하는 뷰입니다. 제품 사진, 가격, 상세 설명, 리뷰 등을 볼 수 있습니다.

    이 모든 뷰는 동일한 제품 데이터 (모델)를 사용하지만, 사용자에게 보여지는 방식 (뷰)는 다릅니다. 사용자는 원하는 뷰를 선택하여 제품 데이터를 탐색할 수 있습니다.

    이와 같이, MVC 패턴의 다중 지원은 동일한 데이터를 다양하게 표현할 있어 사용자 경험을 향상시키는데 중요한 역할을 합니다.

이런 장점들로 인해, MVC 애플리케이션, 모바일 애플리케이션, 그리고 데스크톱 애플리케이션 개발 다양한 영역에서 널리 사용되는 디자인 패턴입니다. 하지만 모든 상황에서 MVC 가장 적합한 선택이라는 것은 아닙니다. 예를 들어, 매우 간단한 소프트웨어나 크고 복잡한 시스템에는 MVC 패턴이 적합하지 않을 있습니다. 따라서 개발자는 프로젝트의 요구 사항과 복잡성을 고려하여 적절한 디자인 패턴을 선택해야 합니다.

 

* MVC 패턴에서 컨트롤러의 역할을 설명해주세요.

MVC 패턴에서 컨트롤러(Controller)는 매우 중요한 역할을 합니다. 컨트롤러는 사용자의 입력을 처리하고, 그에 따라 모델(Model)과 뷰(View)를 업데이트합니다. 다시 말해, 컨트롤러는 모델과 뷰 사이의 '다리' 역할을 하며, 이들 사이의 상호작용을 관리합니다.

사용자의 요청에 따라, 컨트롤러는 다음과 같은 작업을 수행합니다:

  1. 사용자 입력 처리: 사용자의 요청을 받아 이를 적절한 형식으로 변환하거나 검증합니다.
  2. 모델 업데이트: 사용자의 요청을 통해 필요한 작업을 결정하고, 이에 따라 모델을 업데이트합니다. 예를 들어, 데이터를 추가, 수정, 삭제하는 등의 작업을 수행할 수 있습니다.
  3. 뷰 업데이트: 모델의 상태가 변경되면, 이를 반영하여 뷰를 업데이트합니다. 이는 사용자에게 최신 정보를 제공하기 위함입니다.

따라서, 컨트롤러는 애플리케이션의 비즈니스 로직을 처리하고, 모델과 사이의 상호작용을 조정하는 핵심적인 역할을 합니다. 이를 통해, 애플리케이션의 코드 구조가 명확해지고, 컴포넌트의 역할이 분명해집니다.

 

* MVC와 비슷한 다른 디자인 패턴을 사용해 본 경험이 있나요? 그렇다면, 그 경험에 대해 설명해주세요.

MVVM(Model-View-ViewModel)과 MVP(Model-View-Presenter)는 MVC와 비슷한 패턴으로 애플리케이션을 구조화하는 데 사용됩니다.

  1. MVVM(Model-View-ViewModel): MVVM Microsoft 의해 개발되었으며 주로 WPF, Silverlight, nW.js 같은 XAML 기반 애플리케이션에 사용됩니다. 또한, 최근에는 AngularJS, Vue.js 같은 프론트엔드 프레임워크에서도 널리 적용되고 있습니다. MVVM View Model 사이에 ViewModel이라는 새로운 개념을 도입하여 View Model 의존성을 더욱 낮추는 것을 목표로 합니다. ViewModel Model 데이터를 View 사용하기 적합한 형태로 변환하며, 반대로 View 입력을 Model 이해할 있는 형태로 변환합니다.
  2. MVP(Model-View-Presenter): MVP 패턴은 원래 Taligent 의해 개발되었으며 주로 Android 개발에서 인기가 있습니다. MVP Model, View, Presenter 가지 요소로 구성되며, 패턴에서 Presenter MVC Controller 유사한 역할을 하지만, View Model 사이에 강력한 중재자 역할을 합니다. Presenter View Model 사이의 모든 상호작용을 관리하며, View Model 직접 통신하지 않도록 합니다. 이로 인해 View Model 사이의 의존성이 거의 없어져 코드의 재사용성과 테스트 용이성이 향상됩니다.

* MVC 패턴을 사용하면서 겪었던 어려움은 무엇이었나요? 그리고 그 문제를 어떻게 해결했나요? 

  1. 복잡성: MVC 패턴을 적용하면 프로그램의 전체 구조가 복잡해질 있습니다. 컴포넌트(Model, View, Controller) 정확하게 분리하려고 하면 코드의 양이 증가하고, 개발과 디버깅 과정이 복잡해질 있습니다. 이러한 문제를 해결하기 위해, 컴포넌트의 역할과 책임을 명확히 정의하고, 내에서 이에 대한 공통된 이해를 공유하는 것이 중요합니다.
  2. 테스트의 어려움: Controller 사용자 입력과 모델의 상태 변화를 중재하는 역할을 하기 때문에, 복잡한 애플리케이션에서 Controller 코드가 매우 복잡해질 있습니다. 이로 인해 Controller 테스트가 어려워질 있습니다. 문제는 단위 테스트와 모의 객체(Mocking) 사용하여 해결할 있습니다.
더보기

성능 이슈: MVC 패턴을 사용하면, 데이터 변경 감지와 화면 렌더링이 자주 발생할 수 있습니다. 이로 인해 성능 저하가 발생할 수 있습니다. 이 문제는 View와 Model 간의 상호작용을 최소화하고, 필요할 때에만 화면을 업데이트하는 등의 방법으로 해결할 수 있습니다.

React와 같은 modern JavaScript 프레임워크를 사용하면, Virtual DOM을 활용하여 실제 DOM에 발생하는 변화를 최소화하고, 효율적으로 렌더링을 관리할 수 있습니다.

React는 Virtual DOM과 diffing 알고리즘을 사용하여, 상태가 변경되었을 때 필요한 부분만을 효과적으로 업데이트합니다. 즉, 변경된 부분만 실제 DOM에 반영되므로 성능 저하 문제를 크게 개선할 수 있습니다.

이렇게 React 같은 라이브러리나 프레임워크를 사용하면, MVC 패턴의 일부 문제점을 해결하고 애플리케이션의 성능을 향상시킬 있습니다. 그러나, 이렇게 사용할 때에도 컴포넌트의 역할과 상호작용을 이해하고, 적절하게 코드를 구조화하는 것이 중요합니다.

 

'Mockterview' 카테고리의 다른 글

더티체킹 (Dirty Checking)  (0) 2023.05.19
JPA(Java Persistence API)  (0) 2023.05.19
Override(오버라이딩) 와 Overload(오버로딩)  (0) 2023.05.19
Call by reference  (0) 2023.05.18
DI와 IoC, Bean pt.1  (0) 2023.05.18

오버라이드(Override)와 오버로드(Overload)는 프로그래밍에서 주로 사용되는 개념으로,  클래스 내에서 메서드의 이름을 재사용하면서도 다양한 기능을 제공할  있게 해주는 중요한 객체지향 프로그래밍의 특징입니다.

 

1. 오버라이딩(Override) : 오버라이딩은 상속 관계에 있는 두 클래스 사이에서 발생합니다. 부모 클래스에서 정의한 메서드가 자식 클래스에서 다시 정의되는 경우를 오버라이딩이라고 합니다. 이 때, 메서드의 이름, 매개변수의 수와 타입, 반환 타입은 동일해야 합니다. 오버라이딩은 자식 클래스가 부모 클래스의 특정 메서드의 동작을 변경하거나 확장하려 할 때 사용됩니다.

예를 들어, "Animal" 클래스에 "move"라는 메서드가 정의되어 있고, "Bird" 클래스(Animal 클래스를 상속받는)에서 "move" 메서드를 다시 정의하여 "fly"라는 행동을 하도록 할 수 있습니다.

class Animal {
    void move() {
        System.out.println("Animals can move");
    }
}

class Dog extends Animal {
    @Override
    void move() {
        System.out.println("Dogs can walk and run");
    }
}

public class Main {
    public static void main(String args[]) {
        Animal a = new Animal(); // Animal reference and object
        Animal b = new Dog(); // Animal reference but Dog object

        a.move(); // runs the method in Animal class
        b.move(); // runs the method in Dog class
    }
}

 

2. 오버로딩(Overload) : 오버로딩은 같은 클래스 내에서 발생합니다. 같은 이름의 메서드를 여러 개 정의하되, 매개변수의 수나 타입, 또는 둘 다를 변경하여 같은 이름의 메서드를 여러 번 정의하는 것을 오버로딩이라고 합니다. 반환 타입만 다른 경우는 오버로딩이라고 할 수 없습니다.

예를 들어, "add"라는 메서드가 있을 때, 하나는 두 개의 int 타입 매개변수를 받아 그 합을 반환하는 "add(int a, int b)" 메서드, 다른 하나는 두 개의 double 타입 매개변수를 받아 그 합을 반환하는 "add(double a, double b)" 메서드를 정의할 수 있습니다. 이런 식으로 동일한 이름의 메서드가 다른 매개변수를 받는 경우를 오버로딩이라고 합니다.

class DisplayOverloading {
    void disp(char c) {
        System.out.println(c);
    }

    void disp(char c, int num) {
        System.out.println(c + " " + num);
    }
}

public class Main {
    public static void main(String args[]) {
        DisplayOverloading obj = new DisplayOverloading();
        obj.disp('a');
        obj.disp('a', 10);
    }
}

 

오버로딩(Overload)은 메소드의 이름은 같고 매개변수의 개수나 타입이 다른 함수를 정의하는 것을 의미하며 여러개의 서브프로그램 생성을 가능하게 합니다. 오버라이딩(Overriding)은 상위 클래스의 메소드를 하위 클래스가 재정의 하는 것을 의미하며 메소드 이름의 절약과 예상을 가능하게 합니다. 두 기능으로 JAVA에서 다형성을 구현하고, SOLID - OCP, LSP 원칙을 지킬 수 있습니다.

 

Override와 Overload는 메소드(기능)의 재정의 또는 확장을 위한 개념입니다. Override는 부모 클래스의 상속을 받은 자식 클래스에서 확장하는 개념입니다. 메소드의 이름이 일치해야 하고 매개변수의 개수, 순서, 데이터의 타입이 일치해야 하며 return 타입이 일치해야 합니다. 그리고 Overload는 같은 클래스 내부에서 확장하는 개념입니다. 메소드의 이름이 일치해야 한다는 점은 Override와 같지만 Overload는 매개변수의 개수 또는 타입이 달라야 하며 return 타입도 달라야 합니다. 따라서 이 둘은 공통적으로 메소드(기능)의 재정의 또는 확장을 위한 개념입니다. Override는 메소드 하나로 여러 객체를 다루고 객체마다 다른 기능을 사용할 수 있다는 점이 다릅니다.

 

 

* Override와 Overload :

객체 지향 프로그래밍(Object-Oriented Programming, OOP)의 핵심적인 특징인 다형성(Polymorphism)을 구현하는데 사용됩니다. 이 두 개념은 서로 다른 목적과 사용 방식을 가지고 있습니다.

  1. Override:
    Override는 상속 관계에 있는 상위 클래스(base class)의 메소드를 하위 클래스(derived class)에서 재정의하는 것입니다. 이를 통해 상위 클래스의 동작을 하위 클래스가 자신의 필요에 맞게 변경하거나 확장할 수 있습니다. 이를 통해 코드 재사용성을 높이고 코드의 유지 보수를 쉽게 합니다.
    예를 들어, Animal 클래스가 sound() 메소드를 가지고 있고 이를 Dog 클래스와 Cat 클래스가 상속받는다고 가정해 봅시다. Dog 클래스는 sound()를 "Bark"로, Cat 클래스는 "Meow"로 Override할 수 있습니다. 이렇게 하면 같은 sound() 메소드를 호출하더라도 각 동물의 특성에 따른 다른 결과를 얻을 수 있습니다.
  2. Overload:
    Overload는 같은 클래스 내에서 같은 이름의 메소드를 여러 개 가지되, 매개변수의 타입이나 개수가 다른 것을 의미합니다. 이를 통해 같은 기능을 하는 메소드에 대해 서로 다른 타입의 매개변수를 받을 수 있습니다. 즉, Overload는 메소드의 사용자가 같은 동작을 수행하도록 하는 동시에 다양한 입력을 제공할 수 있도록 합니다.
    예를 들어, 두 개의 숫자를 더하는 add() 메소드를 가진 클래스가 있다고 가정해 봅시다. 이 메소드를 Overload하여 정수 두 개를 더하는 메소드와 실수 두 개를 더하는 메소드를 만들 수 있습니다. 이런 방식으로 Overload를 사용하면 코드의 가독성을 향상시키고 사용의 편리성을 높일 수 있습니다.

이렇게 보면, Override Overload 코드의 재사용성을 높이고, 유지 보수를 용이하게 하며, 사용자에게 일관된 사용성을 제공하는데 매우 중요한 도구라는 것을 있습니다.

 

* Method Overriding(오버라이딩) 을 사용할 때 주의해야 할 점은 무엇인가요?

  1. 같은 메소드 시그니처: 오버라이딩 메소드는 원본 메소드와 동일한 메소드 시그니처를 가져야 합니다. 이는 메소드 이름과 매개변수의 순서, 타입, 개수가 동일함을 의미합니다.
  2. 접근 제어자: 오버라이딩 메소드의 접근 제어자는 원본 메소드보다 좁은 범위를 가질 없습니다. 예를 들어, protected 메소드를 private으로 오버라이드 없습니다. 하지만, 반대로 넓은 범위로 변경하는 것은 가능합니다.
  3. 반환 타입: Java 5 이후부터는 공변 반환 타입(covariant return type) 지원합니다. 이는 오버라이딩 메소드가 원본 메소드와 동일한 타입 또는 하위 타입을 반환하도록 허용한다는 것을 의미합니다.
  4. 예외: 오버라이딩 메소드는 원본 메소드에서 선언된 예외와 동일하거나 하위 클래스의 예외만을 던질 있습니다. , 새로운 예외를 추가하거나, 상위 클래스의 예외를 던질 없습니다.
  5. @Override 어노테이션 사용: @Override 어노테이션은 오버라이딩 메소드에 대한 컴파일러의 검사를 요청하는 역할을 합니다. 만약 메소드가 정확하게 오버라이드되지 않았다면 컴파일 에러를 발생시킵니다.
  6. final, static, private 메소드 오버라이딩 불가: final 메소드는 변경될 없으므로 오버라이딩할 없습니다. static 메소드는 클래스 수준에서 작동하므로 오버라이딩할 없습니다. private 메소드는 해당 클래스에서만 접근 가능하므로 오버라이딩할 없습니다.

* Method Overloading(오버로딩) 이 프로그래밍에 어떤 이점을 가져다주나요?

메소드 오버로딩 (Method Overloading)은 여러 가지 방식으로 동일한 메소드 이름을 사용하는 객체 지향 프로그래밍의 기능이며, 다음과 같은 주요 이점들이 있습니다:

  1. 코드의 재사용성 증가: 메소드 오버로딩을 사용하면 동일한 기능을 수행하지만 매개변수 유형이나 개수가 다른 경우, 동일한 이름의 메소드를 여러 번 정의하여 코드의 재사용성을 향상시킬 수 있습니다.
  2. 코드의 가독성 개선: 모든 메소드가 동일한 이름을 가지므로 코드의 가독성이 향상됩니다. 프로그래머는 동일한 기능을 가진 메소드를 다양한 매개변수를 사용하여 호출할 수 있으므로 코드 이해가 더 쉬워집니다.
  3. 컴파일 시간 다형성 제공: 메소드 오버로딩은 컴파일 시간 다형성 또는 정적 다형성을 제공합니다. 이는 매개변수의 유형에 따라 컴파일러가 알맞은 메소드를 결정하고 호출하게 됩니다.
  4. 메소드의 유연성 증가: 메소드 오버로딩을 사용하면 메소드의 유연성이 증가합니다. 같은 메소드를 다양한 타입과 개수의 매개변수로 호출할 수 있습니다.

예를 들어, add()라는 메소드가 있다고 , 정수 개를 더하는 함수와 실수 개를 더하는 함수 모두 add라는 이름을 가질 있으며, 이렇게 하면 코드의 가독성과 유연성이 증가하게 됩니다.

 

* Method Overloading Constructor Overloading 차이점에 대해 설명해주세요.

메소드 오버로딩(Method Overloading)과 생성자 오버로딩(Constructor Overloading) 모두 Java에서 다형성(polymorphism)의 형태를 제공하며, 둘 다 이름이 같은 두 개 이상의 메소드나 생성자를 동일한 클래스 내에 정의할 수 있게 합니다. 그러나 두 오버로딩 간에는 몇 가지 중요한 차이점이 있습니다:

  1. 이름: 메소드 오버로딩에서는 메소드가 클래스에 정의된 대로 이름을 가집니다. 그러나 생성자 오버로딩에서 생성자는 항상 클래스 이름과 동일해야 합니다. 따라서, 생성자의 이름은 클래스 이름에 의해 결정되며 변경할 없습니다.
  2. 리턴 타입: 메소드 오버로딩에서 메소드는 명시적인 반환 타입을 가질 있습니다. , 메소드는 값을 반환할 있습니다. 그러나 생성자는 값을 반환할 없으며, 반환 타입을 가질 없습니다. 생성자의 주요 목적은 객체를 초기화하는 것입니다.
  3. 호출 시점: 메소드는 명시적으로 호출되며 필요한 매개변수를 제공해야 합니다. 반면에, 생성자는 new 키워드를 사용하여 객체가 생성될 자동으로 호출됩니다
public class MyClass {
    // Method Overloading
    void myMethod(int x) {
        // code here
    }

    void myMethod(String str) {
        // code here
    }

    // Constructor Overloading
    MyClass(int x) {
        // code here
    }

    MyClass(String str) {
        // code here
    }
}

위의 코드에서 메소드 오버로딩과 생성자 오버로딩을 모두 보여줍니다. myMethod 메소드 오버로딩의 예로, 개의 정수 또는 문자열을 인수로 받습니다. 반면에 MyClass 생성자는 생성자 오버로딩의 예로, 객체 생성 시점에 자동으로 호출됩니다.

 

* 다형성을 구현하는 다른 방법에는 어떤 것들이 있나요? 이것들과 Method Overloading/Overriding 어떻게 다른가요?

다형성(Polymorphism)은 객체 지향 프로그래밍의 핵심 원칙 중 하나로, 동일한 인터페이스를 통해 다양한 형태로 동작하도록 하거나 다른 유형의 객체들을 동일하게 처리하는 기능을 제공합니다. Java에서 다형성을 구현하는 주요 방법들은 다음과 같습니다:

  1. 상속 (Inheritance): 부모 클래스의 특성과 기능을 자식 클래스가 상속받는 기능입니다. 부모 클래스를 기반으로 다양한 자식 클래스를 생성할 수 있어, 상속을 통해 다형성을 구현할 수 있습니다.
  2. 인터페이스 (Interfaces): 인터페이스는 메소드 선언을 모아 둔 추상 타입으로, 클래스가 인터페이스를 구현하면 그 클래스는 인터페이스가 정의하는 모든 메소드를 구현해야 합니다. 서로 다른 클래스가 같은 인터페이스를 구현할 수 있어, 이를 통해 다형성을 구현할 수 있습니다.
  3. Method Overloading and Overriding: 앞서 설명한 바와 같이, 메소드 오버로딩은 같은 이름을 가진 여러 메소드를 매개변수의 유형이나 개수를 바꿔서 정의하는 것이고, 메소드 오버라이딩은 상위 클래스의 메소드를 하위 클래스에서 재정의하는 것입니다. 이들은 같은 이름의 메소드를 사용하면서도 다른 동작을 수행하도록 하는 방식으로 다형성을 구현합니다.

위의 모든 방법들은 다형성을 지원하지만, 그 방식이나 목적은 약간씩 다릅니다. 상속과 인터페이스는 다형성을 클래스 레벨에서 지원하면서, 여러 클래스가 동일한 메소드를 공유하거나 동일한 인터페이스를 따르도록 하는 데 초점을 맞추고 있습니다. 이와 달리 메소드 오버로딩과 오버라이딩은 단일 클래스 내에서 다형성을 지원하면서, 메소드가 다양한 방식으로 동작하도록 하는 데 초점을 맞추고 있습니다.

 

https://vmpo.tistory.com/29

'Mockterview' 카테고리의 다른 글

JPA(Java Persistence API)  (0) 2023.05.19
MVC (Model View Controller)패턴  (0) 2023.05.19
Call by reference  (0) 2023.05.18
DI와 IoC, Bean pt.1  (0) 2023.05.18
스택(Stack)과 큐(Queue)  (0) 2023.05.18
  1. 자바에서는 기본 타입(primitive type) 참조 타입(reference type) 있는데 기본 타입의 경우 자체가 전달되는 call by value이고, 참조 타입의 경우 참조값(, 주소) 전달되는 것이기 때문에 call by reference처럼 보일 있습니다.
  2. 하지만, 정확히 말하면 자바에서는 모든 것이 call by value입니다. 왜냐하면 메소드에 객체를 전달할 객체의 주소를 전달하는 것이 아니라 참조값(, 주소를 가리키는 ) 복사해서 전달하기 때문입니다. 복사된 참조값을 통해 객체의 필드를 변경할 있지만, 복사된 참조값 자체를 변경하더라도 원본 참조값에는 영향을 미치지 않습니다.
  3. 예를 들어, 메소드 내에서 전달 받은 객체를 다른 객체로 바꾸려고 해도 원본 객체는 변하지 않습니다. 이는 참조값이 복사되어 전달되기 때문입니다. 이러한 이유로 자바의 객체 전달 방식은 '참조에 의한 전달(call by value of reference)'라고도 설명합니다.
  4. 따라서, 객체의 상태를 바꾸는 것은 가능하지만(: 메소드 내에서 객체의 필드 값을 변경), 객체 자체를 바꾸는 것은 불가능하다(: 메소드 내에서 새로운 객체를 할당) 것이 자바에서의 call by value call by reference 차이점입니다.

** Call by reference는 여러가지 상황에서 유용하게 사용될 수 있습니다.

  1. 객체의 상태 변경: 함수 내에서 객체의 상태를 변경하려는 경우에 "call by reference"를 사용할 수 있습니다. 예를 들어, 어떤 객체를 메서드에 전달하고, 그 메서드가 객체의 필드를 수정하면 원본 객체도 바뀌게 됩니다. 이는 원본 객체의 상태를 직접 바꿀 수 있다는 장점이 있습니다.
  2. 대용량 데이터 처리: 큰 데이터 구조를 다룰 때 "call by reference"를 사용하면, 데이터를 복사하는 데 소요되는 시간과 메모리를 절약할 수 있습니다. 실제 데이터 대신 참조(주소)만 전달하면 되기 때문에, 메모리 사용량을 크게 줄일 수 있습니다.
  3. 객체 간의 상호작용: 여러 객체가 서로 상호작용하는 시스템에서 "call by reference"를 사용하면, 한 객체가 다른 객체의 상태를 바꾸는 것이 가능해집니다. 예를 들어, 게임에서 플레이어 객체가 아이템 객체를 사용해서 상태를 변경하는 경우 등에 사용할 수 있습니다.

이러한 방식은 프로그램의 구조를 설계할 중요한 요소가 있으며, 상황에 따라 적절하게 사용해야 합니다. , 객체의 상태를 바꾸는 것이 필요하거나, 메모리를 절약하려는 경우 등에 "call by reference" 사용합니다. 그러나 항상 주의해야 점은, "call by reference" 사용하면 원본 객체의 상태를 바꾸게 되므로, 이것이 예상치 못한 부작용 초래하지 않도록 주의해야 합니다.

 

** 부작용의 예

  1. 데이터의 무결성 손실: 원본 데이터를 변경하면, 그 데이터가 정확하게 유지되어야 하는 경우 문제가 발생할 수 있습니다. 예를 들어, 객체의 필드가 특정 조건을 만족해야 하는데, 그 필드를 변경하는 메서드를 통해 그 조건을 위반하게 될 수 있습니다.
  2. 동시성 문제: 멀티스레드 환경에서 같은 객체를 여러 스레드가 동시에 접근하여 변경하려고 하면, 경쟁 조건(race condition)이 발생할 수 있습니다. 이는 데이터의 일관성을 손상시키고 예기치 못한 결과를 초래할 수 있습니다.
  3. 코드의 가독성과 유지보수: 원본 데이터가 변경되면, 코드의 흐름을 따라가기 어려워지고 디버깅이 더 어려워질 수 있습니다. 함수나 메서드는 보통 입력을 받아 출력을 내는 것이 명확하고 예측 가능하지만, 원본 객체를 변경하면 그 함수의 출력이 어떻게 될지 예측하기 어려워집니다.

이러한 이유로, 가능한 "Call by value" 사용하여 원본 데이터를 보호하거나, "Call by reference" 사용할 때는 함수 내에서 원본 데이터를 변경하지 않는 것이 좋습니다. 또는 데이터 변경을 명시적으로 나타내는 설계 패턴 사용할 수도 있습니다.

 

** 데이터 변경을 명시적으로 나타내는 설계 패턴

  1. 데이터 변경을 명시적으로 나타내는 설계 패턴이란, 코드의 특정 부분이 데이터를 변경할 것임을 분명히 명시하는 프로그래밍 방식을 의미합니다. 이는 코드의 가독성을 높이고, 부작용을 최소화하는 도움이 됩니다.
  2. 예를 들어, 함수 또는 메소드 이름을 선택할 "set", "update", "change" 등의 동사를 사용하여 해당 함수/메소드가 데이터를 변경할 것임을 분명히 표시할 있습니다. 이런 네이밍 컨벤션은 데이터 변경이 이루어질 것임을 명확하게 나타내어, 다른 개발자들이 코드를 이해하고 예측하는데 도움이 됩니다.

 

함수의 호출 방식 중 하나로, 참조에 의한 호출 방식입니다. 함수를 호출할 때 인자로 reference(값에 대한 참조 주소, 메모리상의 주소를 담고 있는 변수)를 전달하며 참조 타입(reference type)을 전달합니다. reference를 전달했기 때문에 인자는 일반적으로 전역 변수의 특성을 갖습니다. 그러나 참조 타입의 변수를 함수 내에서 재할당 하게 되면 원본 값이 변경된다는 위험이 있습니다. 이러한 점을 방지 하기 위해서는 reference로 다른 값을 전달하여 사용합니다. 값이 같은 다른 참조값을 만들어 사용하는 것입니다.

 

Call by value란, 값을 호출하는 것을 의미하고, Call by reference란 참조에 의한 호출을 의미합니다. 전달받은 값을 직접 참조하므로 값을 변경할 경우 원본도 같이 변경이 됩니다. JVM에선 힙 영역에 Object 타입의 데이터, 스택 영역에서는 그 데이터의 참조 값이 할당됩니다. 그렇기에 객체의 주소를 참조해 데이터를 수정한다고 해도 원본에는 변경이 없으므로 JAVA에서의 모든 전달 방식은 Call by value입니다.

 JAVA에서는 Call by value만 사용되어지며 메서드에서 특정 값을 참조할 때 Heap영역에 새로운 값을 임시로 복사하여 스택의 주소값이 가리키는 값이 원본이 아닌 복제된 값을 가리키도록 한다.

 

더보기
  1. Call by Value : 메서드가 매개변수로 값을 전달받을 때, 그 값을 복사해서 메서드 내부로 전달하는 방식을 의미합니다. 이 경우, 메서드 내부에서 복사된 값을 변경하더라도 원본 값에는 영향을 주지 않습니다.
  2. Call by Reference : 메서드가 매개변수로 객체의 참조(주소)를 전달받는 방식을 의미합니다. 이 경우, 메서드 내부에서 참조를 통해 객체의 상태를 변경하면 원본 객체에도 영향을 줍니다.

자바에서는 기본 데이터 타입(int, char, double 등)은 call by value 방식으로 전달되며, 객체는 call by value of reference 방식으로 전달됩니다. 즉, 객체의 참조값(주소를 가리키는 값)이 복사되어 전달됩니다. 그렇기 때문에 메서드 내에서 해당 참조를 이용해 객체의 상태를 변경하는 것은 가능하지만, 참조 자체를 변경하는 것은 원본에 영향을 미치지 않습니다.

이때문에 일부는 자바의 객체 전달 방식을 "Call by Reference"라고 이해할 수도 있지만, 본질적으로는 참조값을 복사하여 전달하므로 "Call by Value"로 이해하는 것이 정확합니다.

Java에서는 원시타입/기본타입(정수부동소수점 )  자체가 함수에 전달되고 객체는 참조(메모리 주소) 전달되지만 참조 자체는 값으로 전달됩니다그래서 함수 내에서  참조를 변경해도 원본 참조에는 영향을 미치지 않습니다따라서 Java 모든 전달 방식을 "call by value"라고   있습니다.

  1. IoC(Inversion of Control) :  "제어의 역전"이라는 의미로, 전통적인 프로그래밍에서 객체가 자신의 로직을 제어하는 구조에서 벗어나, 제어의 흐름을 외부(프레임워크나 컨테이너 ) 위임하는 디자인 원칙입니다. 이를 통해 객체 간의 결합도를 줄이고, 모듈 간의 독립성을 높이는데 목적이 있습니다. IoC 프레임워크가 코드의 흐름을 담당하고 개발자는 안에서 필요한 코드를 제공하는 방식으로 작동합니다.Spring 같은 경우 프레임워크가 만들어 놓은 객체를 주입, IoC 컨테이너에서 Bean으로 등록된 객체를 관리합니다.
  1. DI(Dependency Injection) : DI IoC 형태로, "의존성 주입"이라고 합니다. 객체가 서로 의존하는 상황에서, 의존성(객체가 필요로 하는 다른 객체) 직접 생성/관리하는 것이 아니라 외부(컨테이너나 프레임워크 )에서 주입하는 방식을 말합니다. 이를 통해 객체 간의 결합을 느슨하게 만들어 유지보수성과 재사용성을 향상시킬 있습니다.의존성을 주입하는 방법에는 생성자 주입, Setter 주입, 필드 주입 등이 있는데 Spring에서는 생성자 주입을 권장, 생성자 주입은 생성자의 호출 시점에 1회 호출 되는 것이 보장되기 때문에 주입받은 객체가 변하지 않거나, 반드시 객체의 주입이 필요한 경우 강제로 주입하기 위해 사용할 수 있다는 장점이 있습니다.
    객체가 생성될 때 생성자가 호출되며, 그 시점에 의존성이 주입
    -> 객체가 생성되는 시점에 필요한 모든 의존성이 주입되어야 함을 보장하며, 이후에 변경될 수 없다는 특징
    따라서, 생성자 주입은 "반드시 객체의 주입이 필요한 경우"에 주로 사용됩니다. 생성자에서 필요한 의존성을 선언하면, 해당 객체를 생성하려면 반드시 해당 의존성을 제공해야 합니다. 이렇게 하면 필요한 의존성이 없는 상태로 객체가 생성되는 것을 방지할 수 있습니다

** 예시

    1. 자동차(Car) 객체가 엔진(Engine) 객체에 의존하는 상황에, Car 객체 내부에서 Engine 객체를 직접 생성하면 Car Engine 강하게 결합되어 있게 됩니다. 이런 상황에서 Engine 변화가 Car에도 영향을 미치게 되어 유지보수가 어려워집니다. 반면, DI 사용하면 Engine 객체는 외부에서 Car 객체에 주입되므로, Car Engine 생성 생명 주기에 대해 신경 필요가 없게 되며 이는 객체의 결합도를 낮춥니다.
    2. UserService 클래스가 UserRepository 인터페이스를 사용해 DB에서 사용자를 조회하는 상황을 생각해봅시다. UserService 클래스에서 UserRepository 구현체를 직접 생성하게 되면, UserService UserRepository 특정 구현체에 의존하게 됩니다. 그러나 DI 사용하면, UserService UserRepository 인터페이스에만 의존하게 되고, 실제 사용할 구현체는 프레임워크가 런타임에 주입해줍니다. 이런 식으로 DI 통해 클래스 간의 의존성을 낮출 있고, 코드의 유연성과 재사용성을 높일 있습니다.
    3. 이와 같은 방식은 특히나 유닛 테스트에서 유용하게 사용됩니다. 의존성 주입을 통해 테스트 환경에서는 실제 객체 대신 모의 객체(mock object) 쉽게 주입할 있기 때문입니다.
    4. Java Spring 프레임워크와 같은 모던 프레임워크들은 이런 IoC DI 원칙을 광범위하게 적용하고 있습니다. 이를 통해 개발자는 더욱 모듈화된 코드를 작성하고, 쉽게 유닛 테스트를 실행할 있습니다.

IoC는 의존할 메소드나 객체의 호출을 개발자가 결정하는 것이 아닌 프레임워크에서 결정하는 것으로 “제어의 역전”라는 의미를 갖습니다. 기존에는 클래스 내부에서 의존할 객체를 직접 생성하고 객체 메소드를 호출 했다면 Spring 같은 프레임워크에서는 제어권을 프레임워크에 위임하여 프레임워크가 만들어 놓은 객체를 주입합니다. Spring 같은 경우 IoC 컨테이너에서 Bean으로 등록된 객체를 관리합니다.

DI는 IoC(제어의 역전)의 개념을 구현하기 위한 디자인 패턴이자 객체 의존관계를 외부에서 주입 시키는 방법입니다. 의존성을 주입하는 방법에는 생성자 주입, Setter 주입, 필드 주입 등이 있는데 Spring에서는 생성자 주입을 권장하고 있습니다. 생성자 주입은 생성자의 호출 시점에 1회 호출 되는 것이 보장되기 때문에 주입받은 객체가 변하지 않거나, 반드시 객체의 주입이 필요한 경우 강제로 주입하기 위해 사용할 수 있다는 장점이 있습니다.

 

DI(Dependency Injection)란 스프링이 다른 프레임워크와 차별화되어 제공하는 의존 관계 주입 기능으로, 객체를 직접 생성하는 게 아니라 외부에서 생성한 후 주입 시켜주는 방식이다. DI(의존성 주입)를 통해서 모듈 간의 결합도가 낮아지고 유연성이 높아진다. IoC(Inversion of Control)란 "제어의 역전" 이라는 의미로, 말 그대로 메소드나 객체의 호출작업을 개발자가 결정하는 것이 아니라, 외부에서 결정되는 것을 의미한다. IoC는 제어의 역전이라고 말하며, 간단히 말해 "제어의 흐름을 바꾼다"라고 한다. 객체의 의존성을 역전시켜 객체 간의 결합도를 줄이고 유연한 코드를 작성할 수 있게 하여 가독성 및 코드 중복, 유지 보수를 편하게 할 수 있게 한다.

 

 

제어의 역전 (IoC: Inversion of Control)은 프로그램의 제어 흐름 구조가 뒤바뀌는 것을 의미합니다.

  • 일반적으로 개발자가 필요한 객체를 생성하여 사용하는 것이 일반적이지만, IoC에서는 이를 뒤집어, 용도에 맞는 객체를 시스템이 제공하고 개발자는 그냥 사용합니다. 이를 "의존성 주입(Dependency Injection, DI)"이라고 부릅니다.
  • 실생활 예제로, 가위를 사용하는 경우를 들 수 있습니다. 가위를 직접 만드는 것이 아니라, 가위를 사용하는 용도에 따라 다양한 가위를 사용하는 것과 같습니다.

스프링 프레임워크와 IoC/DI:

  • 스프링 프레임워크는 DI를 지원하여, 개발자가 직접 객체를 생성하지 않고 필요한 객체를 스프링 프레임워크가 제공합니다.
  • 스프링에서 관리하는 이러한 객체를 '빈(Bean)'이라고 부르며, '빈'들은 스프링 IoC 컨테이너에 의해 관리됩니다.

스프링 '빈' 등록과 사용

  • '빈'의 등록은 @Component 어노테이션을 클래스 위에 붙여서 수행하며, 이 클래스는 스프링 서버가 시작될 때 스프링 IoC 컨테이너에 저장됩니다.
  • '빈'의 이름은 클래스의 이름을 사용하되, 첫 글자를 소문자로 변경합니다.
  • '빈'을 사용하기 위해선, @Autowired 어노테이션을 사용하여 스프링 IoC 컨테이너에 의존성 주입을 요청합니다.
  • @Autowired는 멤버변수 선언 위에 또는 '빈'을 사용할 함수 위에 붙일 수 있습니다.
  • 이 어노테이션은 스프링 IoC 컨테이너에 의해 관리되는 클래스에서만 사용 가능합니다.
  • 특정 조건에서는 @Autowired 어노테이션을 생략할 수 있습니다.

추가적인 주요 개념:

  • @ComponentScan: 스프링이 자동으로 빈을 등록할 클래스를 찾을 위치를 지정합니다.
  • @Bean: 직접 객체를 생성하여 빈으로 등록하는데 사용됩니다.
  • ApplicationContext: 스프링 IoC 컨테이너의 인터페이스로, 스프링 컨테이너의 기본적인 기능을 제공합니다. 빈을 찾거나 빈에 접근하는 등의 역할을 합니다.
  • @RequiredArgsConstructor: Lombok 라이브러리에서 제공하는 어노테이션으로, final로 선언된 모든 필드에 대한 생성자를 자동으로 생성합니다. 이는 DI를 위해 사용될 수 있습니다.

따라서 스프링에서는 IoC와 DI를 통해 개발자가 직접 객체를 생성하고 관리하는 부담을 덜고, 개발자는 비즈니스 로직에 집중할 수 있게 합니다. 이를 통해 코드의 가독성과 유지보수성이 향상됩니다.

 

2023.05.23 - [Mockterview] - DI와 IoC pt.2