HandlerMapping은 요청을 처리할 Controller를 찾아주는 역할을 합니다. HandlerMapping은 요청 URL을 보고, 어떤 Controller가 요청을 처리할지 결정합니다.
HandlerAdapter는 HandlerMapping에서 찾은 Controller를 실행합니다. Controller를 실행하기 위해선 Controller의 메소드를 호출해야 합니다. HandlerAdapter는 Controller가 요청을 처리할 수 있는 형태로 변환하고, Controller의 메소드를 실행합니다.
Controller는 요청을 처리하고, 응답을 반환하는 역할을 합니다. Controller는 주로 @Controller 어노테이션을 이용해 생성됩니다. Controller는 요청을 받은 후, 비즈니스 로직을 실행하고, Model 객체에 데이터를 담아 View로 전달합니다.
ViewResolver는 Controller가 반환한 View 이름을 참조하여 실제 View 객체를 생성합니다. ViewResolver는 View 이름을 보고, 어떤 View를 사용할지 결정합니다.
View는 Controller가 전달한 Model 객체를 이용해 응답을 생성합니다. View는 요청을 처리한 후, 응답을 생성하여 사용자에게 반환합니다.
Model은 Controller가 사용하는 데이터를 담는 객체입니다. Controller는 비즈니스 로직을 실행한 후, Model 객체에 데이터를 담아 View로 전달합니다.
Interceptor는 DispatcherServlet이 Controller를 실행하기 전, 후에 실행되는 클래스입니다. Interceptor는 요청에 대한 로깅, 인증, 권한 체크 등의 역할을 수행합니다.
CSR은 클라이언트 측에서 렌더링을 처리하는 방식입니다. 즉, 서버는 필요한 데이터만 전송하고 클라이언트는 이를 받아서 HTML, CSS 및 JavaScript를 사용하여 렌더링합니다. CSR 방식은 초기 로딩 시간이 빠르고 사용자 경험이 좋지만 검색 엔진 최적화(SEO)에 불리합니다.
SSR은 서버 측에서 렌더링을 처리하는 방식입니다. 즉, 서버는 클라이언트에게 HTML, CSS 및 JavaScript를 포함한 완전한 페이지를 제공합니다. SSR 방식은 초기 로딩 시간이 느리지만 검색 엔진 최적화(SEO)에 유리합니다.
CSR과 SSR 방식의 가장 큰 차이점은 렌더링을 처리하는 위치입니다. CSR은 클라이언트 측에서 렌더링하므로 초기 로딩 시간이 빠르고 사용자 경험이 좋지만 검색 엔진 최적화(SEO)에 불리합니다. 반면, SSR은 서버 측에서 렌더링하므로 초기 로딩 시간이 느리지만 검색 엔진 최적화(SEO)에 유리합니다.
어떤 방식을 선택할지는 프로젝트의 목적과 요구사항에 따라 다릅니다. 만약 검색 엔진 최적화(SEO)가 중요하다면 SSR 방식을 선택하는 것이 좋습니다. 그러나 초기 로딩 시간이 중요하다면 CSR 방식을 선택하는 것이 좋습니다.
검색 엔진 최적화(SEO), 초기 로딩 시간, 렌더링을 처리하는 위치
@RestController 애너테이션은 @Controller와 @ResponseBody를 합친 것입니다. 즉, 해당 클래스의 모든 메소드에 @ResponseBody를 적용하는 것과 같습니다. 이 애너테이션을 사용하면 반환값이 HTTP 응답 본문으로 직접 전송됩니다.
@RequestMapping 애너테이션은 요청 URI를 매핑하는 데 사용됩니다. 클래스 레벨과 메소드 레벨에서 사용할 수 있으며, URI, HTTP 메소드, 요청 헤더 등을 지정할 수 있습니다.
@GetMapping, @PostMapping, @PutMapping, @DeleteMapping 애너테이션들은 각각 GET, POST, PUT, DELETE HTTP 메소드를 처리하는 데 사용됩니다. 이 애너테이션들은 @RequestMapping의 축약형이며, 각각의 HTTP 메소드에 대해 따로 애너테이션을 사용할 수 있습니다.
REST (Representational State Transfer)는 웹 서비스에서 자원을 정의하고, 그 자원에 대한 주소를 지정하는 방법 중 하나입니다. 이것은 HTTP를 기반으로하며, 데이터를 주고 받는 데 필요한 모든 정보를 URI에 포함시키는 것입니다.
RESTful 웹 서비스는 REST 아키텍처를 따르는 웹 서비스입니다. RESTful 웹 서비스는 웹의 기존 기술을 사용하여 클라이언트와 서버 간의 통신을 구현합니다. 이러한 웹 서비스는 다른 프로그래밍 언어 및 플랫폼에서 구현하기 쉬우며, 인터넷의 기존 구조와 호환됩니다.
자원(Resource)
행위(Verb)
표현(Representation)
Stateless
Cacheable
Layered system
Uniform interface
장점
단점
SOAP (Simple Object Access Protocol) 웹 서비스는 XML 기반의 프로토콜입니다. SOAP 웹 서비스는 WSDL (Web Services Description Language)을 사용하여 서비스의 설명을 제공합니다. 이에 반해, RESTful 웹 서비스는 완전히 다른 방식으로 작동합니다. RESTful 웹 서비스는 HTTP를 기반으로하며, 클라이언트와 서버 간의 통신을 위해 자원, 행위 및 표현을 사용합니다. 이러한 차이점은 RESTful 웹 서비스가 다양한 플랫폼에서 구현하기 쉽고, 웹의 기존 구조와 호환됨을 의미합니다.
HATEOAS(하테오스)는 RESTful 웹 API 디자인에서 중요한 역할을 하는 개념입니다. HATEOAS를 활용하면 API 사용자는 응답으로 받은 하이퍼미디어를 기반으로 API와 상호작용할 수 있습니다. 이러한 기능은 웹 애플리케이션에서 매우 흔한 기능입니다. 이번 글에서는 HATEOAS의 개념, 원리, 그리고 활용 사례에 대해 알아보겠습니다.
HATEOAS는 Hypermedia As The Engine Of Application State의 약어로, 애플리케이션의 상태를 관리하는 하이퍼미디어를 엔진으로 사용하는 개념입니다. 이는 클라이언트에게 API의 상태 전이를 제공합니다. 즉, 클라이언트는 API와 상호작용하기 위해 필요한 정보를 서버에서 받아올 수 있으며, 이를 통해 API와 상호작용할 수 있습니다. 이러한 방식으로 HATEOAS는 RESTful API의 자기 기술적인 설명( self-descriptive)을 가능하게 합니다.
HATEOAS의 원리는 간단합니다. API의 응답으로 클라이언트에게 전달되는 메시지에는 API에서 실행 가능한 작업과 다음 상태로 이동하기 위한 링크가 포함되어 있습니다. 이러한 링크를 따라가면 클라이언트는 API와 상호작용할 수 있는 다음 단계로 이동할 수 있습니다. 이러한 방식으로 API는 클라이언트에게 API 자체의 상태를 관리하는 방법을 제공하며, 클라이언트는 API의 상태 전이를 따라가며 상호작용합니다.
HATEOAS는 많은 웹 애플리케이션에서 활용되고 있습니다. 예를 들어, GitLab API는 HATEOAS를 활용하여 API 사용자가 API와 상호작용하는 방법을 제공합니다. 또한, Netflix에서는 Spring HATEOAS를 사용하여 API의 상태를 관리하는 방법을 제공하고 있습니다. 이러한 방식으로 HATEOAS는 API의 디자인을 보다 유연하게 만들어줍니다.
Spring MVC에서는 예외 처리를 위한 다양한 방법을 제공합니다. 그 방법들 중에서도 가장 많이 사용되는 방법은 @ExceptionHandler
, @ControllerAdvice
, 그리고 @ResponseStatus
어노테이션을 이용한 방법입니다.
@ExceptionHandler
어노테이션을 이용한 예외 처리는 특정 컨트롤러 내에서 발생하는 예외를 처리하기 위한 방법입니다. 이 어노테이션을 사용하면 특정 예외가 발생했을 때 해당 메서드가 실행되어 예외를 처리할 수 있습니다.
@ControllerAdvice
어노테이션을 이용한 예외 처리는 여러 개의 컨트롤러에서 발생하는 예외를 처리하기 위한 방법입니다. 이 어노테이션을 사용하면 전역적으로 예외를 처리하는 클래스를 정의할 수 있습니다.
@ResponseStatus
어노테이션을 이용한 예외 처리는 예외가 발생했을 때 HTTP 응답의 상태 코드를 설정하기 위한 방법입니다. 이 어노테이션을 사용하면 예외가 발생했을 때, 응답의 상태 코드를 지정할 수 있습니다.
Spring Boot에서는 @RestControllerAdvice
어노테이션을 이용한 예외 처리를 제공합니다. 이 어노테이션을 사용하면 전역적으로 예외를 처리하는 클래스를 정의할 수 있습니다.
Spring MVC에서 예외 처리를 할 때 주의해야 할 점들이 있습니다. 예를 들어, 예외 처리기에서 예외를 발생시키면 안 되며, 예외 처리기에서도 예외가 발생할 수 있으므로 이를 잘 처리해야 합니다. 또한, 예외 처리기를 설정할 때 순서도 중요합니다. 예외 처리기의 순서를 잘못 설정하면 원치 않는 결과를 얻을 수 있습니다.
데이터베이스(Database)는 많은 데이터들이 존재하는데 이러한 데이터들은 서로 관계가 있을 수 있습니다. 이러한 관계를 표현해주는 것이 외래 키(Foreign Key)입니다. 외래 키는 데이터베이스에서 매우 중요한 개념 중 하나입니다. 이번 글에서는 외래 키에 대해서 알아보도록 하겠습니다.
외래 키는 다른 테이블의 기본 키(primary key)를 참조하는 열(column) 또는 열의 집합입니다. 이를 통해 두 테이블 간의 관계를 맺을 수 있습니다. 예를 들어, 학생 테이블과 성적 테이블이 있다면 학생 테이블의 기본 키인 학생 ID를 외래 키로 갖는 성적 테이블이 있을 수 있습니다.
외래 키를 사용하는 이유는 다음과 같습니다.
외래 키(Foreign Key)를 사용할 때 주의할 점은 다음과 같습니다.
외래 키에는 다음과 같은 제약 조건이 있습니다.
외래 키는 데이터베이스에서 중요한 개념 중 하나입니다. 외래 키를 사용하면 데이터 무결성을 유지하고, 관계를 표현하며, 데이터 검색을 빠르게 할 수 있습니다. 하지만, 외래 키를 사용할 때에는 주의해야 할 점들이 있습니다. 이러한 제약 조건들을 잘 이해하고, 외래 키를 적절히 사용하면 데이터베이스를 효율적으로 관리할 수 있습니다.
JPA(Java Persistence API)는 자바에서 제공하는 ORM(Object-Relational Mapping) 기술의 표준 스펙입니다. ORM은 객체와 관계형 데이터베이스 간의 매핑을 지원하여 객체 지향 설계와 관계형 데이터베이스를 연결하는 기술입니다. 이번에는 JPA에서 제공하는 엔티티 간 연관 관계 매핑에 대해 알아보겠습니다.
엔티티는 데이터베이스에서 데이터를 저장할 수 있는 단위입니다. JPA에서 엔티티는 객체를 말하며, 클래스를 정의하여 사용합니다. 엔티티의 필드는 데이터베이스의 컬럼과 매핑됩니다.
엔티티 간의 관계를 매핑하여 객체를 참조하는 방식입니다. 연관 관계 매핑은 객체 간의 관계를 매핑하는 것으로 데이터베이스의 외래 키(foreign key)와 유사합니다.
연관 관계 매핑에는 다음과 같은 종류가 있습니다.
일대일 관계는 한 쪽 엔티티가 다른 쪽 엔티티와 하나의 관계만 가지는 것을 의미합니다. 예를 들어, 회원(Member)과 회원의 프로필(Profile)이 있을 때 회원은 하나의 프로필만 가질 수 있습니다.
일대다 관계는 한 쪽 엔티티가 다른 쪽 엔티티와 여러 개의 관계를 가지는 것을 의미합니다. 예를 들어, 하나의 팀(Team)에 여러 명의 회원(Member)이 있을 때, 팀은 여러 명의 회원을 가질 수 있습니다.
다대일 관계는 여러 쪽 엔티티가 한 쪽 엔티티와 관계를 가지는 것을 의미합니다. 예를 들어, 여러 명의 회원(Member)이 하나의 팀(Team)에 속해있을 때, 회원은 하나의 팀에만 속할 수 있습니다.
다대다 관계는 여러 쪽 엔티티가 다른 쪽 엔티티와 여러 개의 관계를 가지는 것을 의미합니다. 예를 들어, 여러 명의 회원(Member)이 여러 개의 프로젝트(Project)에 참여할 때, 회원과 프로젝트는 다대다 관계를 가집니다.
@Entity
public class Order {
@Id
@GeneratedValue
private Long id;
@ManyToOne
@JoinColumn(name = "customer_id")
private Customer customer;
...
}
@Entity
public class Customer {
@Id
@GeneratedValue
private Long id;
@OneToMany(mappedBy = "customer")
private List<Order> orders;
...
}
위 코드에서 Order 엔티티는 ManyToOne 어노테이션을 사용하여 Customer 엔티티와 연관 관계를 맺고 있습니다. 이때, 관계의 주인은 Order 엔티티이며, JoinColumn 어노테이션을 사용하여 외래 키를 매핑하고 있습니다. 반면, Customer 엔티티는 mappedBy 속성을 사용하여 관계의 주인이 아니라는 것을 표시하고 있습니다. 이때, mappedBy 속성의 값으로는 Order 엔티티에서 매핑한 customer 필드의 이름을 사용하고 있습니다. 이렇게 함으로써, Order 엔티티에서는 customer 필드를 통해 Customer 엔티티와의 관계를 조회할 수 있으며, Customer 엔티티에서는 orders 필드를 통해 Order 엔티티와의 관계를 조회할 수 있습니다.
💡 mappedBy 속성을 사용하여 연관관계의 주인을 설정한 경우, 역방향에서 연관된 엔티티를 조회할 때는 쿼리문이 실행되지 않습니다. 따라서 해당 예시에서는 Order 엔티티를 조회하는 경우 Customer 엔티티의 조회 쿼리문만 실행됩니다.
@Entity
public class Customer {
@Id
@GeneratedValue
private Long id;
@OneToMany(mappedBy = "customer", cascade = CascadeType.ALL)
private List<Order> orders;
...
}
@Entity
public class Order {
@Id
@GeneratedValue
private Long id;
@ManyToOne
@JoinColumn(name = "customer_id")
private Customer customer;
...
}
위 코드에서 Customer 엔티티는 OneToMany 어노테이션을 사용하여 Order 엔티티와 일대다 관계를 맺고 있습니다. 이때, cascade 속성을 CascadeType.ALL로 설정하면 Customer 엔티티에 대한 작업이 Order 엔티티에도 전파되어 Order 엔티티도 함께 생성, 수정, 삭제됩니다.CascadeType.ALL (전파)인 경우, Customer 엔티티를 삭제할 때 해당 고객이 생성한 모든 주문 정보도 함께 삭제됩니다. 따라서 Customer 엔티티를 삭제하는 쿼리문은 다음과 같습니다.
DELETE FROM customer WHERE id = ?
Customer 엔티티를 삭제할 때, 해당 고객이 생성한 모든 주문 정보도 함께 삭제되게 됩니다. 이때, cascade 속성을 설정하지 않으면 Customer 엔티티를 삭제할 때 주문 정보는 삭제되지 않습니다.
@Entity
public class Customer {
@Id
@GeneratedValue
private Long id;
@OneToMany(mappedBy = "customer", cascade = CascadeType.ALL, fetch = FetchType.LAZY)
private List<Order> orders;
...
}
@Entity
public class Order {
@Id
@GeneratedValue
private Long id;
@ManyToOne(fetch = FetchType.EAGER)
@JoinColumn(name = "customer_id")
private Customer customer;
...
}
위 코드에서 Customer 엔티티는 OneToMany 어노테이션을 사용하여 Order 엔티티와 일대다 관계를 맺고 있습니다. 이때, fetch 속성을 FetchType.LAZY로 설정하면 Customer 엔티티를 조회할 때 연관된 Order 엔티티는 지연로딩되어 필요한 시점에 가져옵니다.FetchType.LAZY (지연로딩)인 경우, Customer 엔티티를 조회할 때 연관된 Order 엔티티는 필요한 시점에 가져옵니다. 따라서 Customer 엔티티를 조회하는 쿼리문은 다음과 같습니다.
SELECT * FROM customer WHERE id = ?
반면, Order 엔티티는 ManyToOne 어노테이션을 사용하여 Customer 엔티티와 다대일 관계를 맺고 있습니다. 이때, fetch 속성을 FetchType.EAGER로 설정하면 Order 엔티티를 조회할 때 연관된 Customer 엔티티는 즉시로딩되어 함께 조회됩니다.
FetchType.EAGER (즉시로딩)인 경우, Order 엔티티를 조회할 때 연관된 Customer 엔티티는 즉시 함께 조회됩니다. 따라서 Order 엔티티를 조회하는 쿼리문은 다음과 같습니다.
SELECT o.*, c.* FROM order o JOIN customer c ON o.customer_id = c.id WHERE o.id = ?
즉, 위 코드에서는 Customer 엔티티를 조회할 때 연관된 Order 엔티티는 필요한 시점에 가져오고, Order 엔티티를 조회할 때 연관된 Customer 엔티티는 즉시 함께 조회됩니다.
연관 관계 매핑 시 주의할 점은 다음과 같습니다.
JPA(Java Persistence API)는 자바 진영의 ORM(Object Relational Mapping) 기술 표준입니다. 이번 글에서는 JPA에서 단일 엔티티 매핑 방법에 대해 알아보겠습니다.
JPA에서 엔티티 매핑은 객체와 데이터베이스 테이블 간의 매핑을 의미합니다. 객체를 데이터베이스에 저장하거나 데이터베이스에서 객체를 읽어올 때 이 매핑 정보를 참조하여 작업을 수행합니다.
JPA에서 가장 간단한 형태의 엔티티 매핑 방법으로, 하나의 엔티티 클래스와 하나의 테이블을 매핑하는 방법입니다. 즉, 엔티티 클래스 하나가 데이터베이스의 테이블 하나와 매핑되어 데이터를 주고받습니다. 이 방식은 주로 단순한 데이터 모델에서 사용됩니다.
JPA에서 단일 엔티티 매핑을 위해 사용하는 어노테이션은 다음과 같습니다.
JPA에서 단일 엔티티 매핑을 위해 엔티티 클래스를 생성하고 활용하는 방법은 다음과 같습니다.
영속성 컨텍스트(Persistence Context)란, JPA에서 엔티티(Entity)를 관리하는 논리적인 개념입니다. JPA는 영속성 컨텍스트를 통해 엔티티를 관리하며, 이를 통해 데이터베이스와의 작업을 보다 쉽고 효율적으로 할 수 있습니다.
영속성 컨텍스트를 사용하면, 엔티티를 데이터베이스에 저장하거나 조회할 때 데이터베이스와의 I/O를 줄일 수 있습니다. 또한, 영속성 컨텍스트는 1차 캐시를 가지고 있어 같은 엔티티를 반복해서 조회할 때 데이터베이스에서 가져오는 것이 아니라, 1차 캐시에서 가져오기 때문에 애플리케이션의 성능을 향상시킬 수 있습니다.
영속성 컨텍스트는 엔티티마다 생명주기를 가지고 있습니다. 생명주기는 총 4가지로, 비영속, 영속, 준영속, 삭제 상태가 있습니다. 비영속 상태는 JPA에서 관리하지 않는 상태이며, 영속 상태는 JPA에서 관리하는 상태입니다. 준영속 상태는 영속성 컨텍스트에서 분리된 상태이며, 삭제 상태는 데이터베이스에서 삭제된 상태입니다.
영속성 컨텍스트와 데이터베이스는 내부적으로 동기화(Synchronization)를 수행합니다. 트랜잭션을 커밋할 때, 영속성 컨텍스트는 변경된 엔티티를 데이터베이스에 동기화합니다. 이때, 영속성 컨텍스트는 데이터베이스에 쿼리를 전송하기 전에 엔티티의 상태를 확인하고 변경된 내용이 있으면 쿼리를 생성하여 전송합니다.
Spring JDBC, Spring Data JDBC, Spring Data JPA는 모두 스프링 프레임워크에서 제공하는 데이터 액세스 기술입니다. 그러나 각각의 기술은 목적과 사용 방법이 조금씩 다릅니다.
ORM은 객체 지향 프로그래밍 언어에서 사용하는 객체와 관계형 데이터베이스 간의 데이터를 매핑하는 기술입니다.