이 글은 향로님의 [스프링부트로 웹 서비스 출시하기]를 참고하며 프로젝트를 만들며 정리한 글입니다.
👉 기본 환경
- Language: Java
- DB: MySQL
- IDE: IntelliJ
Test를 통과한 method에 대해 실제로 작동하는지 확인하고자 함
😮 Front가 구현되어있지 않기에 PostMan을 사용하여 Request를 전달
🙂 준비물
WebRestController.java
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
|
@RestController
@RequiredArgsConstructor
public class WebRestController {
private final PostRepository postRepository;
// final 키워드가 붙은 Field는 객체가 생성되는 시점에 반드시 값이 초기화 되어야 함
// 'final' 이 붙은 Field 중에 초기화 되지 않은 모든 Field를 Argument로 설정
// Spring 권장: 생성자를 통한 의존성 주입
// 한번 의존성을 주입받은 객체는 프로그램이 끝날때 까지 변하지 않는 특징을 가지므로 불변성(immutable)을 표시해주는 것이 좋기 때문
// 스프링 프레임워크 4.3 버전부터 클래스에 단 하나의 생성자만 존재하면, 그 생성자에 @Autowired 어노테이션을 생략해도 스프링이 자동으로 의존성 주입을 수행
@GetMapping("/hello")
public String hello(){
return "Hello, IT World!";
}
@PostMapping("/post")
public void savaPost(@RequestBody PostSaveDTO postSaveDTO){
postRepository.save(postSaveDTO.toEntity());
}
}
|
postRepository.save()를 실행할 API 생성
⭐ Entity가 아닌 DTO를 사용한 이유
수많은 서비스 클래스나 비지니스 로직들이 Entity 클래스를 기준으로 동작
Entity 클래스가 변경되면 여러 클래스에 영향을 끼치는데, Request와 Response용 DTO는 View를 위한 클래스라 정말 자주 변경이 필요
프로그램 안정성을 위해 View Layer와 DB Layer를 철저하게 역할 분리를 하는게 좋음
실제로 Controller에서 여러 테이블을 조인해야할 경우가 빈번하기 때문에 Entity 클래스만으로 결과값을 표현하기가 어려운 경우가 많음
꼭꼭 Entity 클래스와 Controller에서 쓸 DTO는 분리해서 사용
@RequestBody
클라이언트의 HTTP 요청 본문(body)을 Java 객체로 변환해주는 어노테이션
- HTTP 프로토콜은 클라이언트가 서버에 데이터를 보낼 때, HTTP 메시지의 본문에 담아 전송
- 데이터는 일반적으로 JSON 형태로 전송되며, @RequestBody 어노테이션은 이 JSON 데이터를 Java 객체로 변환하는 역할
PostSaveDTO.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
@Getter @Setter
// Controller에서 @RequestBody로 외부에서 데이터를 받는 경우엔 기본 생성자 + set메소드를 통해서만 값이 할당
@NoArgsConstructor
public class PostSaveDTO {
private String title;
private String content;
private String author;
public Posts toEntity(){
return Posts.builder()
.title(title)
.content(content)
.author(author)
.build();
}
}
|
🙂 Setter를 사용하는 이유
Controller에서 @RequestBody로 외부에서 데이터를 받는 경우엔 기본생성자 + set메소드를 통해서만 값이 할당되므로 이때만 setter를 허용
🙂 @NoArgsConstructor를 사용하는 이유
Java에서 객체를 생성할 때 기본적으로 생성자를 호출하게 되며, 개발자가 별도의 생성자를 정의하지 않은 경우 컴파일러는 기본 생성자를 자동으로 추가
🤓 DTO 파일의 위치
- 여러 패키지에서 공유해야 하는 상황이면 별도의 dto 패키지 사용
- 특정 서비스 계층까지만 사용되면, 해당 서비스 계층에 dto클래스를 함께 저장
- 특정 repository까지 해당 dto가 사용되면 해당 repository 계층에 dto클래스를 함께 사용
⭐ 가급적 다른 패키지와 연관을 줄이도록 하는 방식이 좋은 방식
PostRepository.java
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
32
33
34
35
36
37
38
39
40
41
42
43
44
|
@Repository // DB에 접근하는 Component
@RequiredArgsConstructor
@Transactional(readOnly = true)
// JPA에서 모든 데이터 변경이나 로직은 transaction 안에서 이뤄져야 함 → @Transactional(spring) 활용
// 조회: readOnly=true, 성능 최적화
public class PostRepository {
private final EntityManager em;
@Transactional
public Long save(Posts post){
if(post.getId()==null){
em.persist(post);
// 영속화되기 전까지 item_id=null, item_id=null → 새로 생성한 객체 → 신규 등록
} else {
em.merge(post);
// 이미 등록된 id가 있을 경우 → 새롭게 입력된 값으로 전부 대체
}
return post.getId();
// post 객체는 고유의 ID(PK)를 갖고 DB에 저장됨 → 해당 ID를 반환하여 참조할 수 있도록 함
}
public Posts findOne(Long id) {
return em.find(Posts.class, id);
}
public List<Posts> findAll(){
return em.createQuery("select p from Posts p", Posts.class)
.getResultList();
}
@Transactional
public void deleteAll() {
em.createQuery("delete p from Posts p")
.executeUpdate();
/*
* JPQL DELETE 쿼리를 실행하면 데이터베이스에서 레코드가 삭제되지만, 영속성 컨텍스트에 해당 엔티티가 여전히 존재할 수 있음
* executeUpdate를 호출하면 엔티티 매니저와 영속성 컨텍스트를 동기화
* 이렇게 하면 영속성 컨텍스트에서 삭제된 엔티티를 더 이상 관리하지 않음
*
* */
}
}
|
😮 Service 파일에서 처리해야하는데, 포스팅 내용을 따라 가다가 Repository로 바로 진행하게 됨
- 수정 예정
Postman
🤹 결과
MySQL
Hibernate
1
2
3
4
5
6
7
8
|
Hibernate:
insert
into
posts
(author,content,title,id)
values
(?,?,?,?)
|
🔗 소스 코드
📚 참고 자료
'PlayGround > AWS 연습' 카테고리의 다른 글
[Project] mini1 - 애플리케이션 실행 전 스크립트 작성 (0) | 2023.10.06 |
---|---|
[Project] mini1 - Ajax DB 통신과 fail 처리 (1) | 2023.10.04 |
[Project] mini1 - View 구현을 위한 JSP 설정 (1) | 2023.10.03 |
[Project] mini1 - Test Code 작성 (0) | 2023.10.01 |
[Project] mini1 - MySQL, JPA 연동 (0) | 2023.09.30 |