게시물 작성
HTTP메서드 : POST
Endpoint URL : /api/user/posts
작업 내용 : 새 게시물 생성
CREATE는 CRUD(Create, Read, Update, Delete)에서 데이터를 생성하는 작업을 의미하며 웹 애플리케이션에서는 주로 사용자가 입력한 데이터를 데이터베이스에 저장하는 기능을 구현한다.
예를 들어, 게시물 작성, 회원가입, 상품 등록 등이 이에 해당 한다.
요구 사항
/usr/article/doAdd 요청에 대한 컨트롤러,서비스,리포지토리의 기능을 이해해 보자.
-
- 요청 및 매개변수 처리 : 사용자에게 게시물의 제목과 내용을 입력. 제목,내용 외에 필요한 필드를 정의
- 유효성 검사 : 입력값을 검증 null이나 빈문자 검사. 길이제한. 특수문자처리.
- 데이터 저장 : 엔티티를 정의하고 post테이블 생성. 트랜잭션관리. ID자동증가문제
- 에러 처리 및 예외 처리 : 유효성검사,DB저장,기능동작 중 실패시 처리.
- 작성자 정보 저장
개념 정리
웹이나 프론트엔드에서 JSON이나 URL로 요청이 들어왔을때 MVC 모델에 따라 처리되는 과정을 정리해보자.
클라이언트 요청: 프론트엔드에서 JSON이나 URL로 요청이 들어옴.
컨트롤러 계층: 요청을 받아 서비스 계층으로 데이터를 전달. 요청을 처리하기 전 유효성 검사 수행.
- 클라이언트 요청 처리: 클라이언트(프론트엔드)로부터 들어오는 요청(예: HTTP 요청)을 수신하여 적절한 메서드와 연결한다.
-URL 매핑: @RequestMapping, @PostMapping, @GetMapping 등의 어노테이션을 사용하여 특정 URL 요청과 컨트롤러 메서드를 매핑한다. - 요청 데이터 수집 및 변환: 클라이언트에서 보낸 데이터를 자바 객체로 변환하거나 파라미터를 받아 처리한다.
-@RequestParam: URL에서 요청 파라미터를 받아오는 데 사용.
-@RequestBody: JSON과 같은 형식으로 요청된 데이터를 객체로 변환. - 유효성 검사: 서비스 계층에 데이터를 전달하기 전에 데이터의 유효성을 확인한다. 유효성 검사는 주로 입력된 값이 null이거나 공백인지 확인하거나, 특정 패턴(이메일 형식, 숫자 범위 등)에 맞는지 확인하는 작업을 포함한다.
-@Valid와 BindingResult를 통해 데이터 유효성을 검사할 수 있다. - 서비스 계층 호출: 유효한 데이터를 서비스 계층으로 전달하여, 비즈니스 로직을 처리할 수 있게 한다.
- 응답 반환: 요청 처리가 끝나면 클라이언트에게 결과를 반환한다. 주로 JSON 형태로 데이터를 응답하거나, 특정 뷰(HTML, JSP 등)를 렌더링하여 반환할 수 있다.
서비스 계층: 비즈니스 로직을 처리하고, 레포지토리 계층에 데이터베이스 작업을 요청.
- 비즈니스 로직 처리
- 비즈니스 로직은 애플리케이션의 핵심 규칙이나 데이터 처리 로직을 말한다. 예를 들어, 게시물을 생성할 때 게시물 제목이 중복되지 않도록 처리하거나, 입력된 데이터를 가공하는 작업이 여기에 해당한다.
- 컨트롤러에서 받은 데이터를 가공하거나 필요한 추가 작업(예: 날짜 설정, 값 변환 등)을 수행한 후 레포지토리 계층에 전달한다. - 데이터 검증 및 가공
- 데이터가 레포지토리 계층에 전달되기 전에 추가적인 유효성 검사나 데이터 가공 작업을 수행할 수 있다.
- 서비스 계층에서만 검증해야 하는 규칙이나 조건이 있는 경우 이를 처리한다. - 레포지토리 계층과의 상호작용
- 데이터베이스 작업은 주로 레포지토리 계층에서 수행되지만, 서비스 계층이 그 작업을 요청하는 역할을 한다.
- 데이터를 저장하거나 조회하는 등의 작업을 레포지토리 계층에 위임한다.
- 트랜잭션 관리도 서비스 계층에서 수행할 수 있으며, 여러 레포지토리 작업이 함께 수행되는 경우 트랜잭션을 통해 작업을 하나의 단위로 처리한다. - 트랜잭션 관리
- 서비스 계층에서 트랜잭션을 관리하여 여러 데이터베이스 작업이 모두 성공적으로 완료되거나, 실패 시 롤백하도록 한다. 이를 통해 데이터의 일관성을 보장할 수 있다.
- Spring에서는 @Transactional 어노테이션을 사용하여 트랜잭션을 쉽게 관리할 수 있다.
레포지토리 계층: 데이터베이스에 데이터를 저장.
- 데이터 저장: 데이터베이스에 새로운 게시물을 삽입하는 작업을 수행한다.
- MyBatis와 JPA는 모두 데이터베이스와 상호작용하여 데이터를 저장, 조회, 수정, 삭제하는 ORM(Object-Relational Mapping) 기술이지만, 그 동작 방식과 사용법에서 많은 차이가 있다.
코드 설명
Post 엔티티
게시물 저장에 필요한 데이터를 정의한 엔티티 클래스.
작성자 정보를 저장하기 위해 Account 객체와 연관 관계를 설정한다.
@Entity
public class Post {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@NotBlank(message = "제목은 필수 입력 항목입니다.")
private String title;
@NotBlank(message = "내용은 필수 입력 항목입니다.")
private String content;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "account_id") // 작성자 정보
private Account author;
@CreationTimestamp
private LocalDateTime createdAt;
@UpdateTimestamp
private LocalDateTime updatedAt;
// Getter, Setter, Constructor
}
PostRequestDto
클라이언트 요청 데이터를 수집하는 DTO 클래스.
이 클래스는 유효성 검사를 추가하여 잘못된 데이터가 서비스로 전달되지 않도록 한다.
public class PostRequestDto {
@NotBlank(message = "제목을 입력하세요.")
private String title;
@NotBlank(message = "내용을 입력하세요.")
private String content;
// Getter, Setter
}
PostService
비즈니스 로직을 처리하는 서비스 계층.
로그인된 사용자의 정보를 작성자로 설정하고 DTO데이터를 set으로 엔티티로 변환하여 레파지토리 계층으로 보낸다.
@Service
@RequiredArgsConstructor
public class PostService {
private final PostRepository postRepository;
@Transactional
public Long createPost(PostRequestDto requestDto, Account author) {
Post post = new Post();
post.setTitle(requestDto.getTitle());
post.setContent(requestDto.getContent());
post.setAuthor(author); // 작성자 설정
return postRepository.save(post).getId();
}
}
PostController
클라이언트 요청을 처리하는 컨트롤러 계층.
로그인된 사용자의 정보를 @AuthenticationPrincipal을 통해 가져와 작성자로 설정.
@RestController
@RequestMapping("/api/user/posts")
@RequiredArgsConstructor
public class PostController {
private final PostService postService;
@PostMapping
public ResponseEntity<?> createPost(
@Valid @RequestBody PostRequestDto requestDto,
BindingResult bindingResult,
@AuthenticationPrincipal PrincipalDetails principal) {
if (bindingResult.hasErrors()) {
return ResponseEntity.badRequest().body(bindingResult.getFieldError().getDefaultMessage());
}
Long postId = postService.createPost(requestDto, principal.getAccount());
return ResponseEntity.ok(Map.of("status", "success", "postId", postId));
}
}
PostRepository 데이터 저장 과정
- postRepository.save(post) 호출:
- JPA는 Post 엔티티의 상태를 확인.
- @Id 필드 값이 null이면 INSERT 쿼리를 생성.
- @Id 필드 값이 존재하면 UPDATE 쿼리를 생성.
- 이미 Post 엔티티 객체의 필드 값들이 설정되어 있으므로 별도의 set 호출이 필요하지않다.
- JPA는 Post 엔티티의 상태를 확인.
- 엔티티 매핑 후 데이터베이스 작업:
- save() 메서드 내부에서 JPA가 EntityManager.persist() 또는 EntityManager.merge()를 호출하여 데이터를 저장.
- 엔티티 객체의 상태를 기반으로 적절한 SQL 쿼리를 생성하고 실행.
개발 중 생길 수 있는 기본적 의문들
Q: 로그인된 사용자가 작성자로 어떻게 확인하여 저장 되는건지??
A 로그인된 사용자는 Spring Security를 통해 인증되며, 컨트롤러에서@AuthenticationPrincipal로 가져온 PrincipalDetails 객체에 포함되어 있다. PrincipalDetails는 현재 로그인된 사용자의 계정 정보를 포함하고 있으며 이를 통해 Account 객체를 추출하여 Post 엔티티의 작성자로 설정한다.
Q: 게시물 작성시 보통 입력받는 데이터 외에 자동설정 해주는 데이터는 무엇인지?
A
- 작성 시간 (createdAt) @CreationTimestamp 어노테이션을 사용해 게시물이 생성될 때 자동으로 현재 시간을 설정.
- 수정 시간 (updatedAt) @UpdateTimestamp 어노테이션을 사용해 게시물이 수정될 때 자동으로 현재 시간을 설정.
- 작성자 정보 (author) 로그인된 사용자의 정보를 작성자로 자동 설정.
- 게시물 ID @GeneratedValue 어노테이션을 통해 데이터베이스에서 자동 생성.
Q: 작성 기능동작시 DTO의 사용이유와 변환 시점이 언제인지?
A
DTO 사용 이유:
- 클라이언트 요청 데이터를 엔티티와 별도로 관리하여 보안을 강화하고, 불필요한 데이터를 차단.
- DTO 클래스에 유효성 검사 어노테이션을 추가하여 잘못된 데이터를 초기에 걸러냄.
- 비즈니스 로직과 클라이언트 요청 데이터 처리 로직을 분리하여 유지보수성을 높임.
변환 시점:
클라이언트 요청 데이터를 DTO로 수집한 뒤 서비스 계층으로 전달.
엔티티로의 변환은 서비스 계층에서 이루어짐.
Q: 엔티티 변환 로직에 빌더 패턴이 사용되는 이유와 사용방법은?
A: 엔티티 변환은 별도의 변환 메서드 또는 빌더 패턴을 사용하는 것이 유지보수성에 유리합니다.
public Post toEntity(PostRequestDto requestDto, Account author) {
return Post.builder()
.title(requestDto.getTitle())
.content(requestDto.getContent())
.author(author)
.build();
}
'기능이해' 카테고리의 다른 글
[JAVA | SPRING | MVC | CRUD] 게시물 수정 Update (2) | 2024.12.08 |
---|---|
[JAVA | SPRING | MVC | CRUD] 게시물 전체조회 (0) | 2024.09.28 |
[JAVA | SPRING | MVC | CRUD] 게시물 단건조회 (0) | 2024.09.28 |