일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | |||
5 | 6 | 7 | 8 | 9 | 10 | 11 |
12 | 13 | 14 | 15 | 16 | 17 | 18 |
19 | 20 | 21 | 22 | 23 | 24 | 25 |
26 | 27 | 28 | 29 | 30 | 31 |
- write by chatGPT
- 자바
- Database
- chatGPT's answer
- 인프라
- NIO
- 자바암호
- JVM
- 역학
- GPT-4's answer
- 리눅스
- spring integration
- flet
- python
- 코틀린
- 시스템
- spring data jpa
- 데이터베이스
- 소프트웨어공학
- 유닉스
- 웹 크롤링
- jpa
- oracle
- 파이썬
- write by GPT-4
- android
- Java
- kotlin
- 고전역학
- 자바네트워크
- Today
- Total
기억을 지배하는 기록
Spring Rest API는 처음인데요. 본문
API와 REST API
API (Application Programming Interface)
API는 한 소프트웨어 애플리케이션 또는 시스템이 다른 애플리케이션과 통신하기 위해 정의한 규칙, 프로토콜, 도구의 집합입니다. API를 통해 다른 소프트웨어와 상호작용할 수 있으며, 이는 개발자가 기존의 기능을 재사용하고, 애플리케이션 기능을 확장할 수 있게 해줍니다.
- 예시: 웹 사이트에서 Google Maps의 기능을 사용하려고 할 때, Google Maps API를 통해 지도를 웹 페이지에 표시할 수 있습니다. 이때 Google Maps API는 웹 사이트와 Google Maps 서비스 간의 통신을 가능하게 하는 규칙과 도구를 제공합니다.
REST API
REST API는 웹 상에서 자원(Resource)을 정의하고, 자원에 대한 주소를 URL로 표현하며, HTTP 메소드(GET, POST, PUT, DELETE 등)를 사용하여 해당 자원에 대한 CRUD(Create, Read, Update, Delete) 작업을 수행하는 방법을 정의한 API입니다. REST는 API를 설계하는 데 사용되는 아키텍처 스타일 중 하나로, 자원 기반의 구조를 가집니다.
REST의 기본 원칙:
- Stateless (무상태성): 각 요청은 독립적이며, 클라이언트의 상태 정보를 서버에 저장하지 않습니다.
- Client-Server (클라이언트-서버 구조): 클라이언트와 서버는 서로 독립적으로 발전할 수 있으며, 서로의 요청만을 처리합니다.
- Cacheable (캐시 가능): 클라이언트는 응답을 캐싱할 수 있어야 하며, 캐싱을 통해 네트워크 효율성을 높일 수 있습니다.
- Uniform Interface (통일된 인터페이스): 애플리케이션의 아키텍처를 단순화하고, 상호작용을 일관되게 만듭니다.
- Layered System (계층화된 시스템): 클라이언트는 최종 서버만을 대상으로 요청을 보내며, 중간에 다른 계층이 존재할 수 있습니다.
REST API는 웹 서비스를 구축할 때 널리 사용되며, 다양한 클라이언트(웹 브라우저, 모바일 앱 등)와의 통신을 용이하게 합니다. API는 일반적인 개념으로, REST API는 API를 구현하는 하나의 방식입니다.
예시: 소셜 미디어 플랫폼의 REST API를 사용하여, 특정 사용자의 프로필 정보를 조회(GET), 새로운 게시물을 생성(POST), 게시물을 수정(PUT) 또는 삭제(DELETE)할 수 있습니다.
REST API 개발 가이드
REST API 개발 시 따라야 할 가이드와 모범 사례는 API의 이해도를 높이고, 사용성, 유지보수성, 확장성을 개선하는 데 도움이 됩니다. 여기에는 RESTful 원칙을 따르는 것이 포함되며, 아래는 REST API 개발을 위한 주요 가이드라인입니다.
1. 자원(Resource) 지향 아키텍처
- 명사를 사용하여 엔드포인트를 명명: 자원은 명사를 사용하여 표현해야 합니다. 예를 들어,
/users
,/blogs
와 같이 자원을 나타내는 경로를 사용합니다. - 계층적 구조 사용: 자원 간의 관계를 URL 경로에 반영합니다. 예:
/blogs/123/posts
는 ID가 123인 블로그의 모든 게시물을 나타냅니다.
2. HTTP 메소드 활용
- CRUD 작업에 대응하는 HTTP 메소드 사용:
GET
(읽기),POST
(생성),PUT
/PATCH
(수정),DELETE
(삭제)와 같은 HTTP 메소드를 적절히 사용하여 자원에 대한 작업을 명확히 합니다. - 멱등성(Idempotence) 유지:
GET
,PUT
,DELETE
는 멱등성을 가져야 합니다. 같은 요청을 여러 번 수행해도 결과가 동일해야 합니다.
3. 상태 코드 활용
- 적절한 HTTP 상태 코드 반환: 요청의 성공 또는 실패 여부를 나타내는 적절한 HTTP 상태 코드를 응답에 포함시킵니다. 예를 들어,
200 OK
,201 Created
,400 Bad Request
,404 Not Found
,500 Internal Server Error
등을 사용합니다.
4. 데이터 형식 명시
- Content-Type 명시: API가 지원하는 데이터 형식(
application/json
,application/xml
등)을Content-Type
헤더를 통해 명시합니다. JSON을 기본으로 사용하는 것이 일반적입니다.
5. 버전 관리
- API 버전 관리: API를 변경할 때는 이전 클라이언트와의 호환성을 유지하기 위해 버전을 관리해야 합니다. URL 내에 버전 정보를 포함시키는 방법(예:
/api/v1/blogs
)이 일반적입니다.
6. HATEOAS (Hypermedia as the Engine of Application State)
- HATEOAS 적용 고려: 응답 내에 다음 작업을 위한 링크를 포함하여 클라이언트가 동적으로 상호작용할 수 있도록 합니다. 이는 REST의 성숙도 모델 중 가장 높은 수준을 나타냅니다.
7. 보안과 인증
- HTTPS 사용: 모든 API 통신은 HTTPS를 통해 암호화되어야 합니다.
- 인증 및 권한 부여: API 접근을 제어하기 위해 OAuth, JWT(JSON Web Tokens) 등의 인증 방식을 고려합니다.
8. 문서화 및 테스트
- API 문서화: Swagger(OpenAPI), Postman 등을 사용하여 API를 명확하게 문서화하고, 외부 개발자가 쉽게 이해하고 사용할 수 있도록 합니다.
- 테스트: 단위 테스트, 통합 테스트를 수행하여 API의 안정성을 보장합니다.
REST API 개발 가이드를 따르면 일관되고, 안정적이며, 사용하기 쉬운 API를 설계할 수 있습니다. 이
러한 원칙과 모범 사례는 개발자 커뮤니티에서 널리 인정받고 있으며, 성공적인 API 개발을 위한 핵심 요소입니다.
Spring Boot Rest API 개발
아래는 Spring Boot와 JPA를 사용하여 간단한 블로그 CRUD REST API를 구현하기 위한 기본적인 구조와 코드 예시입니다. 이 예시에서는 Gradle을 빌드 도구로 사용하고, H2 데이터베이스를 이용하여 블로그의 제목, 내용, 작성일을 관리합니다.
1. 프로젝트 설정
build.gradle
파일에 필요한 의존성을 추가합니다:
plugins {
id 'org.springframework.boot' version '2.5.4'
id 'io.spring.dependency-management' version '1.0.11.RELEASE'
id 'java'
}
group = 'com.example'
version = '0.0.1-SNAPSHOT'
sourceCompatibility = '11'
repositories {
mavenCentral()
}
dependencies {
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
implementation 'org.springframework.boot:spring-boot-starter-web'
runtimeOnly 'com.h2database:h2'
testImplementation 'org.springframework.boot:spring-boot-starter-test'
}
test {
useJUnitPlatform()
}
2. 엔티티 생성
BlogPost
엔티티를 src/main/java/com/example/demo/model
에 생성합니다:
package com.example.demo.model;
import javax.persistence.*;
import java.util.Date;
@Entity
public class BlogPost {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String title;
private String content;
@Temporal(TemporalType.TIMESTAMP)
private Date creationDate = new Date();
// 생성자, 게터, 세터 생략
}
3. 리포지토리 인터페이스 생성
BlogPostRepository
인터페이스를 src/main/java/com/example/demo/repository
에 생성합니다:
package com.example.demo.repository;
import com.example.demo.model.BlogPost;
import org.springframework.data.jpa.repository.JpaRepository;
public interface BlogPostRepository extends JpaRepository<BlogPost, Long> {
}
4. 서비스 레이어 생성
BlogPostService
인터페이스와 구현체 BlogPostServiceImpl
을 src/main/java/com/example/demo/service
에 생성합니다:
package com.example.demo.service;
import com.example.demo.model.BlogPost;
import java.util.List;
public interface BlogPostService {
BlogPost createBlogPost(BlogPost blogPost);
List<BlogPost> getAllBlogPosts();
BlogPost getBlogPostById(Long id);
void deleteBlogPost(Long id);
}
package com.example.demo.service;
import com.example.demo.model.BlogPost;
import com.example.demo.repository.BlogPostRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.List;
@Service
public class BlogPostServiceImpl implements BlogPostService {
@Autowired
private BlogPostRepository blogPostRepository;
@Override
public BlogPost createBlogPost(BlogPost blogPost) {
return blogPostRepository.save(blogPost);
}
@Override
public List<BlogPost> getAllBlogPosts() {
return blogPostRepository.findAll();
}
@Override
public BlogPost getBlogPostById(Long id) {
return blogPostRepository.findById(id).orElse(null);
}
@Override
public void deleteBlogPost(Long id) {
blogPostRepository.deleteById(id);
}
}
5. 컨트롤러 생성
BlogPostController
클래스를 src/main/java/com/example/demo/controller
에 생성합니다:
package com.example.demo.controller;
import com.example.demo.model.BlogPost;
import com.example.demo.service.BlogPostService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import java.util.List;
@RestController
@RequestMapping("/api/blogposts")
public class BlogPostController {
@Autowired
private BlogPostService blogPostService;
@PostMapping
public BlogPost createBlogPost(@RequestBody BlogPost blogPost) {
return blogPostService.createBlogPost(blogPost);
}
@GetMapping
public List<BlogPost> getAllBlogPosts() {
return blogPostService.getAllBlogPosts();
}
@GetMapping("/{id}")
public BlogPost getBlogPostById(@PathVariable Long id) {
return blogPostService.getBlogPostById(id);
}
@DeleteMapping("/{id}")
public void deleteBlogPost(@PathVariable Long id) {
blogPostService.deleteBlogPost(id);
}
}
이 코드 예시는 간단한 블로그 CRUD REST API를 구현하는 방법을 보여줍니다. 이 프로젝트 구조와 코드는 Spring Boot, JPA, H2 데이터베이스를 사용하여 블로그 포스트의 생성, 조회, 삭제 기능을 제공합니다.
Spring Boot TestCase 작성
Spring Boot에서 컨트롤러를 테스트하기 위한 단위 테스트는 @WebMvcTest
어노테이션을 사용하여 수행할 수 있습니다. 이 어노테이션은 Spring MVC 인프라스트럭처를 설정하고 컨트롤러 레이어에 집중할 수 있도록 해줍니다. MockMvc
객체를 사용하여 HTTP 요청을 모의로 보내고 결과를 검증할 수 있습니다.
아래는 앞서 만든 BlogPostController
의 각 API 엔드포인트를 테스트하기 위한 예시 코드입니다.
BlogPostControllerTest
package com.example.demo.controller;
import com.example.demo.model.BlogPost;
import com.example.demo.service.BlogPostService;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.http.MediaType;
import org.springframework.test.web.servlet.MockMvc;
import java.util.Arrays;
import java.util.Date;
import java.util.List;
import static org.mockito.BDDMockito.given;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;
@WebMvcTest(BlogPostController.class)
public class BlogPostControllerTest {
@Autowired
private MockMvc mockMvc;
@MockBean
private BlogPostService blogPostService;
private BlogPost blogPost;
@BeforeEach
void setUp() {
blogPost = new BlogPost();
blogPost.setId(1L);
blogPost.setTitle("Test Title");
blogPost.setContent("Test Content");
blogPost.setCreationDate(new Date());
}
@Test
void createBlogPost() throws Exception {
given(blogPostService.createBlogPost(blogPost)).willReturn(blogPost);
mockMvc.perform(post("/api/blogposts")
.contentType(MediaType.APPLICATION_JSON)
.content(new ObjectMapper().writeValueAsString(blogPost)))
.andExpect(status().isOk())
.andExpect(jsonPath("$.title").value(blogPost.getTitle()));
}
@Test
void getAllBlogPosts() throws Exception {
List<BlogPost> allPosts = Arrays.asList(blogPost);
given(blogPostService.getAllBlogPosts()).willReturn(allPosts);
mockMvc.perform(get("/api/blogposts")
.contentType(MediaType.APPLICATION_JSON))
.andExpect(status().isOk())
.andExpect(jsonPath("$[0].title").value(blogPost.getTitle()));
}
@Test
void getBlogPostById() throws Exception {
given(blogPostService.getBlogPostById(1L)).willReturn(blogPost);
mockMvc.perform(get("/api/blogposts/{id}", 1L)
.contentType(MediaType.APPLICATION_JSON))
.andExpect(status().isOk())
.andExpect(jsonPath("$.title").value(blogPost.getTitle()));
}
@Test
void deleteBlogPost() throws Exception {
mockMvc.perform(delete("/api/blogposts/{id}", 1L)
.contentType(MediaType.APPLICATION_JSON))
.andExpect(status().isOk());
}
}
이 코드에서는 @WebMvcTest
를 사용하여 BlogPostController
만 로드하고, @MockBean
을 사용하여 BlogPostService
의 의존성을 모의(Mock) 객체로 대체합니다. 이 방식으로 실제 데이터베이스나 서비스 로직에 의존하지 않고, 컨트롤러 레이어의 행동을 격리하여 테스트할 수 있습니다.
각 테스트 메서드는 MockMvc
객체를 사용하여 HTTP 요청을 모의로 생성하고, 응답을 검증합니다. 예를 들어, createBlogPost
메서드는 블로그 포스트 생성 API를 테스트하고, getAllBlogPosts
메서드는 모든 블로그 포스트를 조회하는 API를 테스트합니다.
서비스 레이어를 단위 테스트하기 위해서는 @SpringBootTest
어노테이션 대신 @ExtendWith(SpringExtension.class)
를 사용해 Spring TestContext Framework를 활성화하고, @MockBean
또는 @Mock
을 사용하여 필요한 의존성을 모의(Mock) 객체로 대체합니다. 이렇게 하면 서비스 레이어를 격리하여 테스트할 수 있으며, 데이터베이스와 같은 외부 시스템에 대한 의존성 없이 순수한 단위 테스트를 수행할 수 있습니다.
BlogPostServiceTest
아래는 앞서 만든 BlogPostService
의 메소드를 테스트하기 위한 예시 코드입니다.
package com.example.demo.service;
import com.example.demo.model.BlogPost;
import com.example.demo.repository.BlogPostRepository;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import java.util.Optional;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.BDDMockito.given;
@ExtendWith(MockitoExtension.class)
public class BlogPostServiceTest {
@Mock
private BlogPostRepository blogPostRepository;
@InjectMocks
private BlogPostServiceImpl blogPostService;
private BlogPost blogPost;
@BeforeEach
void setUp() {
blogPost = new BlogPost();
blogPost.setId(1L);
blogPost.setTitle("Test Title");
blogPost.setContent("Test Content");
}
@Test
void createBlogPost() {
given(blogPostRepository.save(blogPost)).willReturn(blogPost);
BlogPost created = blogPostService.createBlogPost(blogPost);
assertThat(created).isNotNull();
assertThat(created.getTitle()).isEqualTo(blogPost.getTitle());
}
@Test
void getBlogPostById() {
given(blogPostRepository.findById(1L)).willReturn(Optional.of(blogPost));
BlogPost found = blogPostService.getBlogPostById(1L);
assertThat(found).isNotNull();
assertThat(found.getId()).isEqualTo(blogPost.getId());
}
}
설명
@ExtendWith(MockitoExtension.class)
: JUnit 5에서 Mockito를 사용하여 테스트 환경을 설정합니다. 이 어노테이션은 Mockito의 모의 객체를 초기화하는 데 필요합니다.@Mock
:BlogPostRepository
의 인스턴스를 모의 객체로 생성합니다. 이를 통해 실제 데이터베이스에 접근하지 않고 리포지토리의 동작을 모의할 수 있습니다.@InjectMocks
: 모의 객체를 사용하여BlogPostServiceImpl
의 인스턴스를 생성하고, 의존성을 주입합니다. 이렇게 하여BlogPostService
의 메소드를 테스트할 준비가 완료됩니다.given(...).willReturn(...)
: Mockito의 BDD 스타일 API를 사용하여 모의 객체의 행동을 정의합니다. 예를 들어,blogPostRepository.save(blogPost)
가 호출되면blogPost
를 반환하도록 설정합니다.assertThat(...)
: AssertJ의 단언(assertion) 메소드를 사용하여 테스트 결과를 검증합니다. 예를 들어, 생성된 블로그 포스트가null
이 아니고, 제목이 예상 값과 일치하는지 확인합니다.
이 예시는 BlogPostService
의 createBlogPost
와 getBlogPostById
메소드를 테스트합니다. 각 테스트는 서비스 메소드가 올바르게 동작하는지 검증하기 위해 모의된 리포지토리와 함께 실행됩니다. 이 방식을 통해 실제 데이터베이스에 의존하지 않고 순수한 단위 테스트를 수행할 수 있습니다.
서비스 레이어와 리포지토리 레이어를 테스트하는 단위 테스트는 애플리케이션의 비즈니스 로직과 데이터 접근 로직이 예상대로 작동하는지 확인하는 중요한 과정입니다. 아래에서는 스프링 부트에서 BlogPostService
와 BlogPostRepository
를 테스트하기 위한 기본적인 단위 테스트 코드 예시와 함께 설명을 제공합니다.
리포지토리 레이어 테스트
리포지토리 레이어를 테스트할 때는 @DataJpaTest
를 사용하여 JPA 관련 설정만 로드하고, H2와 같은 인메모리 데이터베이스를 사용하여 실제 데이터베이스 작업을 테스트할 수 있습니다.
package com.example.demo.repository;
import com.example.demo.model.BlogPost;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest;
import static org.assertj.core.api.Assertions.assertThat;
@DataJpaTest
public class BlogPostRepositoryTest {
@Autowired
private BlogPostRepository blogPostRepository;
@Test
public void whenSaveBlogPost_thenFindById() {
BlogPost blogPost = new BlogPost();
blogPost.setTitle("Test Title");
blogPost.setContent("Test Content");
blogPostRepository.save(blogPost);
BlogPost foundPost = blogPostRepository.findById(blogPost.getId()).orElse(null);
assertThat(foundPost).isNotNull();
assertThat(foundPost.getTitle()).isEqualTo(blogPost.getTitle());
}
}
@DataJpaTest
는 리포지토리 레이어에 집중된 테스트를 위해 스프링 데이터 JPA 설정을 로드하고, H2 같은 인메
모리 데이터베이스를 사용하여 데이터베이스와의 상호작용을 테스트합니다. 이 예제에서는 BlogPostRepository
의 save
메서드를 사용하여 BlogPost
엔티티를 저장하고, findById
메서드로 저장된 엔티티를 조회하여 결과를 검증합니다.
이렇게 서비스와 리포지토리 레이어의 단위 테스트를 구현함으로써, 애플리케이션의 다양한 컴포넌트가 정상적으로 작동하는지 확인하고, 오류를 조기에 발견하여 수정할 수 있습니다.
Thymeleaf를 사용하여 위에서 구현한 블로그 CRUD REST API를 사용하는 간단한 웹 애플리케이션의 HTML과 컨트롤러 코드를 아래에 제시합니다. 이 예제에서는 블로그 글의 목록을 보여주고, 새로운 글을 작성할 수 있는 기능을 구현할 것입니다.
Thymeleaf를 위한 의존성 추가
먼저, build.gradle
파일에 Thymeleaf 관련 의존성을 추가합니다.
dependencies {
// 기존 의존성들...
implementation 'org.springframework.boot:spring-boot-starter-thymeleaf'
}
HTML 템플릿 생성
src/main/resources/templates
디렉토리에 blogs.html
파일을 생성합니다. 이 파일은 모든 블로그 글을 보여주고, 새로운 글을 작성할 수 있는 링크를 포함할 것입니다.
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>블로그 글 목록</title>
</head>
<body>
<h1>블로그 글 목록</h1>
<div>
<a th:href="@{/blogs/create}">새 글 작성</a>
</div>
<div>
<ul>
<li th:each="blogPost : ${blogPosts}">
<span th:text="${blogPost.title}">제목</span>:
<span th:text="${blogPost.content}">내용</span>,
<span th:text="${blogPost.creationDate}">작성일</span>
<a th:href="@{/blogs/edit/{id}(id=${blogPost.id})}">수정</a>
<a th:href="@{/blogs/delete/{id}(id=${blogPost.id})}">삭제</a>
</li>
</ul>
</div>
</body>
</html>
컨트롤러 생성
Thymeleaf 템플릿을 렌더링하기 위한 컨트롤러를 src/main/java/com/example/demo/controller
디렉토리에 생성합니다. BlogWebController
라는 이름으로 클래스를 만들고, REST API를 호출하여 데이터를 가져온 후 Thymeleaf 템플릿에 전달합니다.
package com.example.demo.controller;
import com.example.demo.model.BlogPost;
import com.example.demo.service.BlogPostService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import java.util.List;
@Controller
public class BlogWebController {
@Autowired
private BlogPostService blogPostService;
@GetMapping("/blogs")
public String listBlogs(Model model) {
List<BlogPost> blogPosts = blogPostService.getAllBlogPosts();
model.addAttribute("blogPosts", blogPosts);
return "blogs"; // blogs.html 템플릿을 렌더링
}
// 추가적인 메소드들 (글 작성, 수정, 삭제)은 이곳에 구현...
}
위의 컨트롤러 코드는 /blogs
경로로 접속했을 때, 모든 블로그 글을 조회하여 blogs.html
템플릿에 렌더링하는 기능을 구현합니다. 실제로 새 글 작성, 수정, 삭제 등의 기능을 추가하려면 각각의 경로와 처리 로직을 컨트롤러에 추가해야 합니다.
이 예시는 Thymeleaf를 사용하여 블로그 글 목록을 보여주는 기본적인 웹 페이지를 구현하는 방법을 보여줍니다. 실제 애플리케이션에서는 사용자 입력 처리, 폼 검증, 에러 처리 등 추가적인 작업이 필요할 수 있습니다.
참고: Controller, Service, DAO(Data Access Object)
Spring 프레임워크에서 Controller, Service, DAO(Data Access Object) 계층을 나누는 주된 이유는 관심사의 분리(Separation of Concerns, SoC) 원칙을 적용하여 애플리케이션의 유지보수성, 확장성, 테스트 용이성을 향상시키기 위함입니다. 각 계층은 독립적인 역할과 책임을 가지며, 이를 통해 코드의 재사용성을 높이고, 변경에 유연하게 대응할 수 있습니다.
Controller 계층
- 용도: 클라이언트의 요청을 받아 처리하고, 적절한 응답을 반환하는 역할을 합니다. 이 계층은 HTTP 요청과 응답을 직접 다루며, 사용자의 입력을 검증하고, 요청에 따른 비즈니스 로직 실행을 Service 계층에 위임합니다.
- 특징: REST API에서는 RESTful 원칙에 따라 리소스에 대한 CRUD(Create, Read, Update, Delete) 작업을 HTTP 메소드(GET, POST, PUT, DELETE)와 매핑하여 정의합니다.
- 예시: 사용자가 웹 애플리케이션의 특정 페이지에 접근할 때, 해당 요청을 받아 적절한 뷰와 모델 데이터를 반환하는 컨트롤러 메소드를 정의할 수 있습니다.
Service 계층
- 용도: 애플리케이션의 비즈니스 로직을 구현합니다. 이 계층은 시스템의 비즈니스 규칙을 캡슐화하고, 다양한 Controller에서 재사용될 수 있는 메소드를 제공합니다. 또한, 트랜잭션 관리도 이 계층에서 수행됩니다.
- 특징: Service 계층은 Controller와 DAO 계층 사이를 연결하는 역할을 하며, 비즈니스 로직의 실행 결과를 Controller에 전달합니다. Service 계층은 데이터의 영속성과는 독립적으로 비즈니스 규칙을 정의합니다.
- 예시: 사용자 정보를 생성하고 검증하는 로직이나, 복잡한 쿼리 결과를 가공하는 로직 등을 Service 계층에서 구현할 수 있습니다.
DAO (Data Access Object) 계층
- 용도: 데이터베이스나 다른 영속성 메커니즘에 접근하는 로직을 캡슐화합니다. 이 계층의 주된 책임은 애플리케이션에서 사용되는 엔티티의 CRUD 작업을 수행하는 것입니다.
- 특징: DAO 계층은 Service 계층으로부터 비즈니스 로직을 분리하여, 데이터베이스 접근 코드의 중복을 방지하고, 데이터베이스 작업을 추상화합니다. 이를 통해 데이터 소스가 변경되어도 Service 계층의 코드 수정 없이 DAO 계층만 수정하여 대응할 수 있습니다.
- 예시: 사용자 정보를 데이터베이스에 저장하거나, 특정 조건에 맞는 데이터를 조회하는 메소드를 DAO 계층에서 구현할 수 있습니다.
'Spring for Beginners' 카테고리의 다른 글
타임리프(Thymeleaf) (1) | 2024.12.02 |
---|---|
Spring Security 는 처음이에요. (1) | 2024.02.27 |
Spring JPA는 처음인데요. (0) | 2024.02.19 |
@SpringBootApplication (1) | 2024.02.13 |
Spring Boot 첫 번째 예제 (0) | 2024.02.13 |