본문 바로가기
PlayGround/AWS 연습

[Project] mini1 - Postman 활용

by HJ0216 2023. 10. 1.

이 글은 향로님의 [스프링부트로 웹 서비스 출시하기] 참고하며 프로젝트를 만들며 정리한 글입니다.

 

2) 스프링부트로 웹 서비스 출시하기 - 2. SpringBoot & JPA로 간단 API 만들기

이번 시간엔 SpringBoot & JPA로 간단한 API를 만들 예정입니다. Tip) 아직 SI 환경에선 Spring & MyBatis 를 많이 사용하지만, 쿠팡/우아한형제들/NHN Entertainment 등 자사 서비스를 개발하는 곳에선 SpringBoot & J

jojoldu.tistory.com

 

 

👉 기본 환경

- Language: Java

- DB: MySQL

- IDE: IntelliJ

 

 

Test를 통과한 method에 대해 실제로 작동하는지 확인하고자 함

😮 Front가 구현되어있지 않기에 PostMan을 사용하여 Request를 전달

 

Postman API Platform | Sign Up for Free

Postman is an API platform for building and using APIs. Postman simplifies each step of the API lifecycle and streamlines collaboration so you can create better APIs—faster.

www.postman.com

 

 

🙂 준비물

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
        (?,?,?,?)
 
 

 

 

 

🔗 소스 코드

 

GitHub - HJ0216/mini1: SpringBoot+JPA Board Mini Project

SpringBoot+JPA Board Mini Project. Contribute to HJ0216/mini1 development by creating an account on GitHub.

github.com

 

📚 참고 자료

 

실전! 스프링 부트와 JPA 활용1 - 웹 애플리케이션 개발 - 인프런 | 강의

실무에 가까운 예제로, 스프링 부트와 JPA를 활용해서 웹 애플리케이션을 설계하고 개발합니다. 이 과정을 통해 스프링 부트와 JPA를 실무에서 어떻게 활용해야 하는지 이해할 수 있습니다., 스프

www.inflearn.com

 

[Java] 불변 객체(Immutable Object) 및 final을 사용해야 하는 이유

클린코드를 읽어도, 이펙티브 자바를 읽어도, 개발을 잘하는 팀의 얘기를 들어도 항상 좋은 코드를 얘기할 때면 불변의 객체를 필연적으로 접하게 되는 것 같습니다. 그래서 이번에는 불변의 객

mangkyu.tistory.com

 

DTO 프로젝트 위치 관련 - 인프런 | 질문 & 답변

안녕하세요orderSimpleQueryDTO의 경우에는 특수한 쿼리를 위한 경로에 위치시켰는데 DTO를 프로젝트에서 일반적으로 어디에 위치시키는지 궁금합니다.강의를 들으면서 팀 프로젝트를 같이 진행하고

www.inflearn.com