본문 바로가기
web/SpringBoot

Spring Boot 3.1.x 으로 RestFul API 서버 만들기(1)

by 뽀리님 2023. 8. 21.

지난시간에 만들었던, Swagger 프로젝트에 이어 endpoint 와 DB 연동후 API 통신이 가능한 서버를 구현해보겠다.

 

 

[ 개발스펙 ]

 OS : MAC Intel Ventura 13.2.1

✔ Tool : IntelliJ IDEA

✔ JDK : OpenJDK 17

✔ Spring Boot 3.1.2

✔ Gradle/Groovy

 

 

DB는 MySQL 로 하였고, Docker로 설치 후 구동하였다. (아래글 참조)

https://ssmyefrin.tistory.com/8

 

Docker 로 MySQL 설치하기

✔ MAC OS 기준입니다. 1. Docker 를 쓰기 위해서는 Docker Hub 계정을 생성해야한다. https://hub.docker.com/ Docker Hub Container Image Library | App Containerization Deliver your business through Docker Hub Package and publish apps and p

ssmyefrin.tistory.com

 

Mybatis를 써도 되지만, 나는 그냥 간단하게 구현할꺼라 굳이 복잡한 쿼리를 쓰지 않는 이상 JPA 를 더 선호한다.

Spring Data JPA를 써서 CRUD 를 다뤄보겠다. (ORM 기술을 적용해보고 싶은 마음도 있음!)

 

 

JPA(Java Persistence API)

  • Java 진영에서 ORM(Object-Relational Mapping) 기술 표준으로 사용하는 인터페이스 모음
  • 자바 어플리케이션에서 관계형 데이터베이스를 사용하는 방식을 정의한 인터페이스
  • 인터페이스 이기 때문에 Hibernate, OpenJPA 등이 JPA 구현함

 

 

1. build.gradle 에 MySQL, JPA 관련 의존성을 추가해준다.

implementation 'mysql:mysql-connector-java:8.0.22'
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
implementation 'org.springframework.boot:spring-boot-starter-validation'

// mapper를 통해 Dto <-> Entity 변환
implementation 'org.mapstruct:mapstruct:1.5.5.Final'
annotationProcessor 'org.mapstruct:mapstruct-processor:1.5.5.Final'

 

 

 

 

2. application.properties(application.yml) 에 DataSource, JPA 설정정보를 추가해준다.

참고로 설정해주지 않으면 아래와같은 에러가 발생한다.

spring.datasource.url 없으면 무조건 embedded datase 자동실행된다고 한다. 따라서 h2 실행되었고, url 없으니 오류 발생함

 

## datasource
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.jdbc-url=jdbc:mysql://localhost:3306/amexDev?useSSL=false&useUnicode=true&characterEncoding=utf8&serverTimezone=Asia/Seoul
spring.datasource.username=root
spring.datasource.password=1234

## JPA 설정
spring.jpa.database=mysql
# SQL 쿼리로그 출력레벨 설정
logging.level.org.hibernate.SQL=debug
# SQL 로그형식 포맷팅여부
spring.jpa.properties.hibernate.format_sql=true
# SQL 내부에 주석추가
spring.jpa.properties.hibernate.use_sql_comments=true
# SQL문에 색상부여
spring.jpa.properties.hibernate.highlight_sql=true
# 바인딩된 파라미터 확인가능
hibernate.type.descriptor.sql=trace

 

(* 참조 : 자동설정된 datasource빈을 사용하지 않고, 직접 config 설정한 빈(DataSourceBuilder(Mybatis)) 사용

같이 사용하기 위해 url 대신 jdbc-url사용하였다.  JPA만 쓴다면 spring.datasource.url 로 써야한다.)

 

 

3. datasource Bean을 등록한다

@Configuration
@RequiredArgsConstructor
public class DatabaseConfig {
    @Bean
    @ConfigurationProperties(prefix="spring.datasource")
    public DataSource mysqlDataSource() {
        return DataSourceBuilder.create().build();
    }

}

 

 

 

사용자 정보 조회를 위해  MySQL 에 user_info 라는 테이블을 만들어주었고 스키마 구조는 아래와같다.

 

- user_info Table 

대충 간략하게 만들었다.

CREATE TABLE `user_info` (

`id` varchar(20) COLLATE utf8mb4_general_ci NOT NULL,

`name` varchar(50) COLLATE utf8mb4_general_ci DEFAULT NULL,

`password` varchar(200) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL,

`birth` varchar(6) COLLATE utf8mb4_general_ci DEFAULT NULL,

`gender` varchar(1) COLLATE utf8mb4_general_ci DEFAULT NULL,

`role` varchar(50) COLLATE utf8mb4_general_ci DEFAULT NULL,

`address` varchar(50) COLLATE utf8mb4_general_ci DEFAULT NULL,

`phone` varchar(50) COLLATE utf8mb4_general_ci DEFAULT NULL,

`email` varchar(100) COLLATE utf8mb4_general_ci DEFAULT NULL,

`pw_count` int DEFAULT '0',

`refresh_token` varchar(500) COLLATE utf8mb4_general_ci DEFAULT NULL,

`create_at` datetime DEFAULT NULL,

`update_at` datetime DEFAULT NULL,

PRIMARY KEY (`id`)

) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;

 

 

 

 

4. 테이블과 매핑된 Entity 를 생성한다.

@Getter
//@NoArgsConstructor(access = AccessLevel.PROTECTED)
@NoArgsConstructor
@AllArgsConstructor
@Builder
@Entity
@Table(name = "user_info")
public class UserEntity {
    @Id
    private String id;
    private String name;
    private String password;
    private String birth;
    private String gender;
    private String role;
    private String address;
    private String phone;
    private String email;
    @Column(name = "pw_count")
    private int pwCount;
    @Column(name = "refresh_token")
    private String refreshToken;
    @CreatedDate
    @Column(name = "create_at")
    private LocalDateTime createAt;
    @LastModifiedDate
    @Column(name = "update_at")
    private LocalDateTime updateAt;

}

 

  • @Entity가 붙은 클래스는 JPA가 관리하는 객체로,  Entity 프록시를 만들어야 하기 때문에 기본생성자는 필수다.
  • 기본생성자를 위해 @NoArgsConstructor 를 써주었는데, 이때 접근 제한 레벨을 따로 걸지 않으면 외부에서 생성자에 쉽게 접근할 수 있게 된다. (유지보수성을 최대화하고 접근가능성을 최소화하기 위해 접근레벨을 PROTECTED 로 거는 것이 좋다.)
  • BUT! 나는 Mapsturct을 이용할 예정이므로, 따로 접근레벨 지정을 하지 않았다.(지정시 오류)
  • @Builder, @AllArgsConstructor : DTO 객체를 따로 생성하여,  Entity 와 서로 바인딩을 시키기위해 모든 생성자 생성 옵션과 빌더패턴을 넣었다.

 

 

SpringBoot에서는 Entity의 기본적인 CRUD가 가능하도록 JpaRepository 인터페이스를 제공한다.

 

Spring Data JPA에서 제공하는 JpaRepository 인터페이스를 상속하기만 해도 되며, 인터페이스에 따로 @Repository등의 어노테이션을 추가할 필요가 없다.

JpaRepository 상속받을 때는 사용될 Entity 클래스와 ID 값이 들어가게 된다. JpaRepository<T, ID>  된다.

 

 

5. UserRepository 생성

public interface UserRepository extends JpaRepository<UserEntity, String> {

}

 

 

 

Entity 는 DB와 데이터를 주고 받을때 사용되므로 View를 위한 DTO 객체를 만들어 역할을 구분하는것이 좋으며, 
MapStruct 라이브러리를 이용하여 DTO <-> Entity 간 객체 Mapping을 처리할 예정이므로 DTO와 Mapper 인터페이스를 작성하도록 하겠다.

 

 

 

6. 데이터 전송 객체 DTO 생성

@Data
@Builder
@Schema(description = "회원정보 DTO")
public class UserDto {
    @Schema(description = "아이디")
    private String id;

    @Schema(description = "이름")
    private String name;

    @Schema(description = "비밀번호")
    private String password;

    @Schema(description = "생년월일")
    private String birth;

    @Schema(description = "성별")
    private String gender;

    @Schema(description = "등급")
    private String role;

    @Schema(description = "주소")
    private String address;

    @Schema(description = "휴대폰번호")
    private String phone;

    @Schema(description = "이메일")
    private String email;

    @Schema(description = "비밀번호카운트")
    private int pwCount;

    @Schema(description = "리프레시토큰")
    private String refreshToken;

    private LocalDateTime createAt; // 생성시간
    private LocalDateTime updateAt; // 수정시간
}
  • @Schema : Swagger에서  문서화 정보를 지정하는 사용된다.

 

 

 

 

7. 공통 Mappper 처리를 위한 Generic Interface 구현

public interface EntityMapper<D, E> {
    E toEntity(final D dto);
    D toDto(final E entity);
    List<D> toDtoList(List<E> entityList);
    List<E> toEntityList(List<D> dtoList);
}

 

 

8. UserMapper 설정

@Mapper
public interface UserMapper extends EntityMapper<UserDto, UserEntity>  {
    UserMapper MAPPER = Mappers.getMapper(UserMapper.class);
}

 

 

여기까지 했다면, 이제 Repositroy 를 통해 user_info 테이블 접근이 가능해졌다!! 이제 서비스단과 컨트롤러 작성만 하면 된다.

 

 

9. UserSerivce 에 로직 추가

/**
 * 사용자정보조회
 * @return
 */
public ResponseEntity<List<UserDto>> getUserList() {
    try {
        List<UserEntity> entityList = userRepository.findAll(); // 모든레코드를 조회한다.
        List<UserDto> userList = UserMapper.MAPPER.toDtoList(entityList); // Entity -> DTO
        return new ResponseEntity<>(userList,HttpStatus.OK);
    } catch (Exception e) {
        log.error("사용자 조회 실패 :" + e.getMessage(), e);
        return new ResponseEntity<>(HttpStatus.INTERNAL_SERVER_ERROR);
    }
}
  • findAll() : 전체 레코드를 불러오는 기본적인 JPA 메소드이다.
  • Mapper 를 통해 DTO로 변환시켜 주었다.

 

9-1. User 테이블에 데이터등록

 

 

10. Controller 에 Endpoint를 추가한다.

/**
 * 사용자정보 조회
 * @return
 */
@GetMapping("/list")
@Operation(summary = "사용자 목록 조회", description = "가입 되어있는 사용자 목록을 조회한다.")
public ResponseEntity<List<UserDto>> getUserList() {
    return userService.getUserList();
}

 

 

이제 Swaager 페이지에서 확인할일만 남았다.😆

구동하여 확인해보자!!

 

등록된 데이터를&nbsp; 잘 가져온다!

 

 

다음엔 JWT를 통한 필터처리와 사용자 인증을 추가할 예정이다.

 

그럼 안뇽 🐾(ฅ•.•ฅ)