728x90

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

 

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

 

728x90
728x90

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

 

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

 

 

해당 포스팅에서는 Repository를 Spring Data JPA를 사용하여, JPARepository를 상속받아서 구현하였지만,JPA 기본 강의에서 배운 EntityManager를 활용해보고자 변경하여 진행하였습니다.

🙂 김영한의 [자바 ORM 표준 JPA 프로그래밍 - 기본편]

 

 

PostRepositoryTest.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
45
46
package com.project.mini1.domain.posts;
 
import static org.assertj.core.api.Assertions.assertThat;
 
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.annotation.Rollback;
import org.springframework.transaction.annotation.Transactional;
 
import java.util.List;
 
@SpringBootTest
public class PostRepositoryTest {
 
    @Autowired
    PostRepository postRepository;
 
    @Test
    @Transactional
    public void 게시글_저장() throws Exception {
        // given
        // 테스트 기반 환경을 구축하는 단계
        Posts post = Posts.builder()
                .title("테스트 제목")
                .content("테스트 본문")
                .author("테스트 저자")
                .build();
 
 
        // when
        // 테스트 하고자 하는 행위 선언
        Long saveId = postRepository.save(post);
        Posts findPost = postRepository.findOne(saveId);
 
        // then
        // 테스트 결과 검증
        assertThat(findPost.getId()).isEqualTo(post.getId());
        // findOne id의 return 값 = save id의 return 값과 같은가
        assertThat(findPost).isEqualTo(post);
        System.out.println("findPost == post: " + (findPost == post));
 
    }
 
}
 
 

@SpringBootTest

    - 스프링과 관련되어 통합테스트를 진행할 때 사용(특히 DB와 연동되어 처리 할 경우 사용)

 

@RunWith(SpringRunner.class)

    - Springboot 3.x대 사용

    - 기본 설정 : JUnit5(Spring Web Dependency 사용 시, 자동으로 추가)

        - @ExtendWith(SpringExtension.class)로 대체 됨(생략 가능)

 

* JUnit: 자바 프로그래밍 언어용 단위 테스트 프레임워크

 

@Test

    - 테스트 메서드임을 선언

@Transactional

    - 해당 범위 내 메서드가 트랜잭션이 되도록 보장, 선언적 트랜잭션

 

Test Code 작성법

1. given

    - 테스트 기반 환경 구축

    - Builder로 인스턴스 생성 후, 변수 선언(추후, 검증 시 활용)

 

2. when

    - 테스트하고자 하는 행위 선언

    - Spring Data JPA를 사용하면 JPARepository를 상속받기 때문에 기본 메서드는 따로 구현하지 않아도 되지만, EntityManger를 사용할 경우, Repository에서 테스트하고자 할 메서드 구현 필수

 

3. then

    - 테스트 결과 검증

    - assertThat

        - 모든 테스트 코드는 asssertThat() 메서드에서 출발
        - AssertJ에서 제공하는 다양한 메서드를 연쇄 호출하며 코드를 작성

 

🤓 추가

JUnit5에는 @After, @Before 대신 @AfterEach, @BeforeEach 사용

    - 테스트 실행 전후로 작업을 추가하여 테스트 단위별로 독립성을 높일 수 있음

    - Test code는 기본적으로 수행 후 자동으로 rollback 진행하므로 cleanup() 작성 X

 

 

 

🔗 소스 코드

 

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

 

Runwith, springboottest어노테이션이 없이 테스트 하면 어떻게 되나요? - 인프런 | 질문 & 답변

이전에는 그 어노테이션 없이 test를 진행했던것같은데이번에는 저 어노테이션이 추가가되어있네요어떤 차이인거죠?runwith 어노테이션은 junit에 내장된 러너를 사용하는 대신 어노테이션에 정의

www.inflearn.com

 

JUnit 5 User Guide

Although the JUnit Jupiter programming model and extension model do not support JUnit 4 features such as Rules and Runners natively, it is not expected that source code maintainers will need to update all of their existing tests, test extensions, and custo

junit.org

 

[JAVA] UnitTest에서 사용하는 AssertJ의 AssertThat이란?

기본적으로 이 단계를 만족하도록 테스트를 작성할 수 있다. 간혹 그렇지 않은 경우도 있지만 초심자라면 더더욱 이것을 따르는 것이 좋다!! 1. given: 어떤 상황이 주어졌을 때(이 데이터 기반으

hseungyeon.tistory.com

 

@Before, @After - JUnit5

아래의 예제 대로 실행을 하면 Before, test,After 가 순차적으로 진행이 되어야한다. public class BeforeAfter { @Before public void SetUp(){ System.out.println("Before"); } @Test void transformation() { System.out.println("test"); } @

deviscreen.tistory.com

 

728x90
728x90

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

 

1) 스프링부트로 웹 서비스 출시하기 - 1. SpringBoot & Gradle & Github 프로젝트 생성하기

많은 웹 서비스 구축하기 강좌들이 Python, NodeJS, Ruby, PHP만 다루고 있습니다. 국내에서 가장 많이 사용하는 언어인 Java로 웹서비스 구축 강좌는 본적이 없습니다. Java는 대부분 로컬에서 CRUD & localh

jojoldu.tistory.com

 

👉 기본 환경

- Language: Java

- DB: MySQL

- IDE: IntelliJ

 

 

게시글은 H2 데이터베이스를 사용하고 있으나, MySQL로 변경하여 진행

    - JPA 강의에서 H2 DB를 사용하기 때문에 접속을 변경하는 게 번거러울 것 같아 MySQL 사용

 

 

🚨 스프링 부트에서는 H2 데이터베이스가 내장되어 있어 별도의 설치 없이 의존성 추가만으로 사용 가능

MySQL에서는 application.properties에 DB 추가 설정 필요

 

application.properties

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# MySQL 설정
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
 
# DB Source URL
spring.datasource.url=jdbc:mysql://<IP>:<Port/<DB>?useSSL=false&useUnicode=true&serverTimezone=Asia/Seoul
 
# DB username
spring.datasource.username=<username>
 
# DB password
spring.datasource.password=<password>
 
# true 설정시 JPA 쿼리문 확인 가능
spring.jpa.show-sql=true
 
# DDL(create, alter, drop) 생성
spring.jpa.hibernate.ddl-auto=update
 
# JPA의 구현체인 Hibernate가 동작하면서 발생한 SQL의 가독성 향
spring.jpa.properties.hibernate.format_sql=true
 
 

 

 

 

🔗 소스 코드

 

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

 

[Spring] Spring Boot 에서 H2 database 사용법 , application.yml 설정

인메모리 데이터 베이스로 디스크 검색보다 자료 접근이 훨씬 빠른 것이 큰 장점입니다. 단점은 매체가 휘발성이기 때문에 DB 서버가 꺼지면 모든 데이터가 유실된다는 단점이 있습니다.테스트

velog.io

 

[Spring Boot] MySQL & JPA 연동 및 테스트 (Gradle 프로젝트)

SpringBoot에서 MySQL 그리고 Spring Data JPA를 연동하는 방법에 대해 알아보도록 하겠습니다. 1. 프로젝트에 의존성 추가하기 build.gradle에 의존성을 아래와 같이 추가해줍니다. dependencies { implementation 'my

dev-coco.tistory.com

 

[SpringBoot] SpringBoot + MySQL + JPA과 연동하기 + 테스트

개요 - SpringBoot에 MySQL을 연동해본다. - SpringBoot에 JPA를 연동해본다. - MySQL과 JPA를 연동 후, 테스트 오브젝트인 Memo 객체를 만들어서, SpringBoot를 실행한다. - (확인포인트) 실행 후에, 정상적으로 설

i5i5.tistory.com

 

728x90