지난시간에 만들었던, 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
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 페이지에서 확인할일만 남았다.😆
구동하여 확인해보자!!
다음엔 JWT를 통한 필터처리와 사용자 인증을 추가할 예정이다.
그럼 안뇽 🐾(ฅ•.•ฅ)
'web > SpringBoot' 카테고리의 다른 글
Spring Boot 3.1.x 으로 RestFul API 서버 만들기(2) (2) | 2023.08.28 |
---|---|
SpringBoot 구조와 원리 (0) | 2023.08.22 |
Spring Boot 3.1.x 환경세팅부터 Swagger 까지(2) (0) | 2023.08.17 |
Spring Boot 3.1.x 환경세팅부터 Swagger 까지(1) (0) | 2023.08.16 |
Spring Boot 3 (0) | 2023.08.16 |