| 일 | 월 | 화 | 수 | 목 | 금 | 토 |
|---|---|---|---|---|---|---|
| 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 |
- Eclipse
- hcj
- html
- GraphQL
- js
- Android 4.1
- 하버드
- 안드로이드
- Android
- 구글
- 코틀린
- 안철수
- kotlin
- springboot
- ActiveX
- linux
- JavaScript
- gradle
- 보안
- 탐지기법
- 개발
- build.gradle
- 리눅스
- 안드로이드 개발
- C++
- 자바
- 노개북
- java
- css
- Today
- Total
꿈소년의 개발 이야기
[스프링 부트 개발자 온보딩 가이드 스터디] Chapter 04 JPA 기반의 To-Do 리스트 REST API 서버 개발 본문
[스프링 부트 개발자 온보딩 가이드 스터디] Chapter 04 JPA 기반의 To-Do 리스트 REST API 서버 개발
fogthegreat 2026. 4. 3. 20:16DAY 4
🔖 오늘 읽은 범위 :
🌱공부 내용: JPA 기반의 To-Do 리스트 REST API 서버 개발
👢쪽수: p.103-p.150
목표: 데이터 영속성 지원 기능 추가.
기능 요구사항: JPA, MySQL 데이터베이스를 이용하여 CRUD 기능 구현.
구현 요구사항
- 엔드포인트: “/api/todos/v2”
- 테스트: CRUD 기능 유닛 테스트 작성.
- 문서화 및 테스트: Swagger 3을 사용해 스키마와 API에 대한 문서화 수행 및 테스트 가능하게 하기.
JPA 이해하기
- 자바 객체와 RDB(관계형 데이터베이스) 간의 데이터를 효율적으로 매핑하고 관리하기 위한 표준 ORM 명세.
- 객체 지향 언어 자바에서 클래스와 디비 테이블 간의 매핑을 쉽게 처리할 수 있도록 돕는 기술.
JPA
- ORM(Object Relational Mapping) 방식
- 왜 필요한가?
- SQL 쿼리 관리 용이성
- 디비 커넥션 관리 문제.
- 복잡한 세부 사항들보다는 비즈니스 로직에 구현 집중.
- JPA 구현체 : JPA 는 명세이자 규약, 인터페이스이다.
- Hibernate 하이버네이트 : 실제 데이터베이스 연동하는 JPA 구현체. 대표적인 JPA 구현체이다.
- EclipseLink
- OpenJPA
- JPA 대안 : JPA 구현체가 아니거나 ORM 방식이 아니지만, 디비 연동에 사용하는 기술들.
- MyBatis : SQL 매퍼 프레임워크. SQL 쿼리를 개발자가 직접 작성하고 제어할 수 있는 구조이다.

스프링 데이터 JPA 가 포함된 스프링부트 애플리케이션 아키텍처 구조
JPA와 Hibernate
- JPA : 자바 애플리케이션에서 ORM 구현을 위한 표준 명세.
- Hibernate : JPA 구현체. 가장 많이 사용함. JPA 명세 구현, 배치 처리, 캐시 전략, 통계 조회 등등의 고급 기능 추가. JPA 표준 API 와 확장 기능.
JPA 애노테이션
| 애노테이션 | 설명 |
|---|---|
| @Entity | 붙여 놓은 클래스를 JPA 엔티티로 명시함. 데이터베이스 테이블과 매핑 된다. |
| @Id | 엔티티의 기본 키(Primary Key) 지정. |
| @GeneratedValue | 기본 키 생성 전략 지정. |
| strategy 속성으로 기본 키 생성 방식을 지정한다. | |
| • GenerationType.AUTO : JPA 구현체에게 맡긴다. | |
| • GenerationType.IDENTITY : 데이터베이스에게 맡긴다. ex) MySQL 의 AUTO_INCREMENT | |
| • GenerationType.SEQUENCE : 시퀀스를 이용해 생성한다. 주로 Oracle 에서 사용함. | |
| • GenerationType.TABLE : 별도의 키 생성 테이블을 통해 키를 생성한다. | |
| @Table | 엔티티와 매핑 될 데이터베이스 테이블을 지정한다. |
| @Column | 엔티티 필드와 데이터베이스 열을 매핑한다. |
| 추가 설정을 지정할 때 사용한다.(길이, null 허용 여부, 고유 제약 등) | |
| @ManyToOne | |
| @OneToMany | 엔티티 사이의 연관 관계 설정. |
| 부모 - 자식 관계를 표현. | |
| @JoinColumn 을 통한 외래 키 지정 가능. | |
| • @ManyToOne : 다대일 관계. | |
| • @OneToMany : 일대다 관계. | |
| @OneToOne | |
| @ManyToMany | 엔티티 사이의 1:1 및 다대다 관계 설정. |
| • @OneToOne : 두 엔티티 간의 1:1 관계. | |
| • @ManyToMany : 다대다 관계. | |
| @JoinTable 을 사용해 중간 테이블을 명시적으로 지정 가능함. | |
| @JoinTable | 다대다 관계(@ManyToMany) 관계에서 중간 테이블을 명시적 지정할 때 사용한다. |
| 1:다(@OneToMany) 또는 1:1(@OneToOne) 관계에서 외래 키 맵핑을 커스텀할 때 사용한다. | |
| 두 테이블 간의 다대다 관계에서 중간 테이블을 명시적으로 지정한다. 또한 외래 키를 직접 설정할 수 있다. |
스프링 데이터 JPA
- 스프링 데이터 프로젝트의 하위 모듈. JPA 를 쉽게 사용할 수 있도록 도와준다.
- 데이터베이스와의 상호 작용하는 코드를 최소화 하고, CRUD 작업을 훨씬 효율적으로 처리한다.
- 쿼리를 직접 작성하지 않아도 인터페이스 메서드의 이름만으로 데이터베이스 작업을 수행할 수 있다.
스프링 데이터 JPA 주요 기능
- Repository 기반 데이터 접근
- CrudRepository, JpaRepository 같은 기본 인터페이스 제공 : 데이터베이스 접근 로직 구현.
- 상속 받아서 정의한 레포지토리
- 인터페이스는 CRUD 기본 작업을 자동 지원함.
- 메서드 이름을 기반으로 쿼리를 자동 생성할 수도 있다.
- 예시
- UserRepository 인터페이스 : JpaRepository 인터페이스 상속 받았으면.
- 메서드 구현체 자동 생성 제공한다.
- findById(Long id) : 주어진 ID를 조건으로 사용자 정보를 조회한다.
- save(User user) : 새 사용자를 추가하거나, 기존 사용자 정보를 수정한다.
- deleteById(Long id) : 주어진 ID에 해당하는 사용자를 삭제한다.
- UserRepository 인터페이스 : JpaRepository 인터페이스 상속 받았으면.
JPQL 과 네이티브 쿼리 지원
- JPQL : Java Persistance Query Language
- SQL 과 비슷하다.
- 엔티티 객체 기준으로 쿼리를 작성한다.
- 더 복잡한 쿼리가 필요한 경우에 사용한다.
- @Query 애노테이션을 사용해서 JPQL 이나 네티이브 쿼리를 직접 작성할 수 있다.
자동 Repository 생성
- Repository 인터페이스를 자동 스캔하고 구현체를 생성한다.
- 스프링부트 환경 하에서 별도 구성 없이 자동 구성된다. 개발자는 인터페이스만 정의 한다. 구현체를 직접 작성하지 않는다.
- @EnableJpaRepositories 애노테이션을 사용해 스캔 범위를 조정할 수도 있다.
트랜잭션 관리 통합
- 스프링 프레임워크의 트랜잭션 관리 기능과 긴밀하게 통합되어 있다.
- @Transactional 애노테이션으로 트랜잭션을 적용할 수 있다.
- Repository 메서드 실행 시, 트랜잭션이 자동으로 관리된다.
- 스프링부트의 높은 생산성과 생산적인 개발 경험에 크게 기여하는 부분임.
DTO 패턴 이해하기
- DTO : Data Transfer Object, 데이터 전송 객체.
- 분산 시스템이나 원격 호출 환경에서 네트워크 오버헤드를 줄이기 위해 여러 데이터를 한 번에 전송하는 것.
- 여러 필드를 개별 호출 하는 것이 아닌, 하나의 객체로 묶어 전달하는 방식.
- 다양한 레이어 간 데이터 전달에 활용.
- 스프링부트 애플리케이션에서 서비스와 컨트롤러 간 데이터 교환에 자주 사용함.
- 외부 데이터 교환에 사용되며 구조가 더 유연하다.
- 외부 또는 클라이언트와의 데이터 교환을 위한 모델.
- 엔티티 Entity
- 데이터베이스 테이블과 1:1 로 매핑되는 도메인 객체.
- 데이터베이스와 직접 상호 작용할 때 사용한다.
- JPA 에서 @Entity 애노테이션이 붙을 때, 해당 객체가 영속성 컨텍스트에서 관리 되도록 설정한다.
- 일반적인 CRUD 중심 개발에서 엔티티는 순수한 데이터 표현 객체로 사용한다.
- 비즈니스 로직은 다른 레이어에 위임한다.
- 구조 변경 시 데이터베이스 스키마에도 영향을 미친다.
- DB를 위한 모델.
- 도메인 주도 설계 DDD
- 엔티티가 자신의 데이터를 처리하는 비즈니스 규칙이나 로직을 내부에 포함하도록 설계한다.

- DTO 와 엔티티는 명확하게 분리하여 사용하는 것이 좋다.
- 시스템의 유연성과 유지보수성을 향상 시킨다.
DTO 사용 이점
- 보안성과 데이터 캡슐화 유지
- 엔티티 정보를 모두 노출하지 않고 필요한 정보만 선택해서 노출할 수 있다.
- 레이어 간 결합도 감소.
- DTO 는 데이터 전송 담당. 엔티티와 분리 되어 있음.
- 비즈니스 로직을 변경하더라도 DTO 는 변경되지 않기 때문에 불필요한 코드 변경이 최소화 된다.
DTO 패턴의 단점
- 원본 객체와 DTO 간의 변환 작업이 필요하다.
- ModelMapper or MapStruct
- 변환 로직이 많아지면 유지 보수에 부담을 주기도 한다.
- 변환 작업은 추가적인 오버헤드 수반하며 성능 저하를 유발할 수도 있다.
- 대규모 트랜잭션 시스템에서는 성능 최적화가 중요한 경우, DTO 사용에 따르는 트레이드 오프를 적절히 고려해야 한다.
프로젝트 초기화
- 도커를 이용하여 MySQL을 WSL2 환경에 설치한다.
도커를 이용한 MySQL 설치 및 설정
- 도커를 이용해 MySQL 8.0 설치
- 도커 사용하는 이유
- 다른 애플리케이션과 격리된 환경에서 실행하여 종속성 충돌을 방지한다.
- 간단한 명령어로 설치 및 배포 가능하다.
- 도커 컨테이너 내에서 의존성 문제를 해결하여 로컬 환경에서 충돌을 방지한다.
- 이로 인해 시스템을 깨끗하게 유지할 수 있다.
MySQL 도커 컨테이너 실행
WSL2 터미널 열기.
MySQL 도커 컨테이너 실행.
실행 명령어
docker run --name mysql-todo \ -e MYSQL_ROOT_PASSWORD=dev_password \ -e MYSQL_DATABASE=todo_db \ -e MYSQL_USER=todo_user \ -e MYSQL_PASSWORD=dev_password \ -p 3306:3306 \ -v mysql-todo-data:/var/lib/mysql \ -d mysql:8.0- 명령어로 인해 실행되는 작업들
- MySQL 8.0 컨테이너 실행.
- todo_db 데이터베이스 생성.
- todo_user 사용자 계정 생성.
- 루트 비밀번호 및 사용자 비밀번호 설정.
- 3306 포트 매핑
- 호스트 3306 포트와 컨테이너 3306 포트 연결. 호스트에서 접근할 수 있도록 함.
- 데이터 영구 저장
- mysql-todo-data 라는 docker 볼륨을 /var/lib/mysql 디렉토리에 매핑하여 MySQL 데이터가 영구적으로 저장된다.
- 명령어로 인해 실행되는 작업들
MySQL 컨테이너 상태 확인
MySQL 컨테이너 정상 동작인지 확인하는 명령어.
docker ps --filter "name=mysql"- 결과 화면에서 해당 컨테이너 목록이 나타나고, STATUS ‘Up’ 으로 표시되면 정상 실행이다.
도커 데스크탑 앱에서 컨테이너 정상 동작 확인.
- 컨테이너스 항목 선택 → 목록에서 Status ‘Running’ 이 표시되면 정상 실행 중.
MySQL 접속 확인
접속 명령어.
docker exec -it mysql-todo mysql -u todo_user -p- 명령어 입력 후 패스워드 입력 창 나타남.
- 위에서 나온 패스워드 입력. ex) dev_password
- 개발 환경에서만 사용하는 암호이므로 운영 환경에서는 더 강력한 암호를 사용해야 함.
- MySQL 셀 접속 완료.
MySQL 셀 접속 후 데이터베이스 생성 확인
SHOW_DATABASES;
테이블 스키마
- 예제 todo 테이블 스키마
| 컬럼 | 데이터 타입(크기) | 제약 | 설명 |
| --- | --- | --- | --- |
| id | BIGINT | AUTO_INCREMENT PRIMARY KEY | 기본 키 |
| title | VARCHAR(255) | NOT NULL | 제목 |
| description | TEXT | | 설명 |
| completed | BOOLEAN | NOT NULL DEFAULT FALSE | 완료 여부 |
| created_at | TIMESTAMP | NOT NULL DEFAULT CURRENT_TIMESTAMP | 레코드 생성 시간 |이 테이블은 미리 생성할 필요 없음.
application.properties 에 적용한 옵션으로 엔티티 클래스에 정의된 대로 테이블 자동 생성 및 스키마 업데이트(필요시)
# Hibernate 가 디비 스키마를 자동 업데이트 하도록 설정. spring.jpa.hibernate.ddl-auto=update
DDL 예시
CREATE TABLE todo_db.todo { id BIGINT AUTO_INCREMENT PRIMARY KEY, -- Todo ID, 자동 증가 기본키 title VARCHAR(255) NOT NULL, -- Todo 제목, 널이 아님. description TEXT, -- Todo 설명 completed BOOLEAN NOT NULL DEFAULT FALSE, -- 완료 여부, 기본값은 false. created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP -- Todo 생성 시간 }
settings.gradle - 프로젝트 명 변경
rootProject.name = ‘변경할 프로젝트 이름’
build.gradle - JPA, MySQL JDBC 드라이버 의존성 추가
JPA, MySQL JDBC 커넥터, TestContainers 의존성 추가.
// Spring Data JPA 의존성 추가 implementation 'org.springframework.boot:spring-boot-starter-data-jpa' // MySQL JDBC 드라이버 의존성 추가. implementation 'mysql:mysql-connector-java:8.0.32' // Import TestContainers for JUnit 5 // 임시 MySQL 컨테이너 생성 및 유닛 테스트 수행을 위한 의존성 추가. testImplementation 'org.testcontainers:junit-jupiter:1.20.1' testImplementation 'org.testcontainers:testcontainers:1.20.1' testImplementation 'org.testcontainers:mysql:1.20.1'
application.properties 수정
스프링부트 애플리케이션 설정과 동작 방식을 정의하고 관리하는 중요 구성 파일.
API 서버가 MySQL에 접근할 수 있도록 여기에 설정을 추가해야 한다.
# 로깅 및 관리 도구에서 식별하기 위한 애플리케이션 이름 설정. spring.application.name=todo-mysql # 디비 URL 설정. spring.datasource.url=jdbc:mysql://localhost:3306/todo_db # 디비 사용자 이름 설정. spring.datasource.username=todo_user # 디비 사용자 비밀번호 설정. spring.datasource.password=dev_password # 디비 접근 드라이버 설정. spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver # Hibernate 가 디비 스키마를 자동 업데이트 하도록 설정. spring.jpa.hibernate.ddl-auto=update # SQL 쿼리 출력 설정. spring.jpa.show-sql=true
JPA 기반 To-Do 리스트 API 서버 구현
- 인 메모리 기반 API 서버 구현: 데이터 영속성 보장되지 않음 ⇒ 서버 종료 또는 재시작 할 경우 데이터가 유실 됨.
엔티티, DTO, 매퍼 작성
- 엔티티: 데이터베이스에 작업을 저장하고 읽을 때 사용.
- DTO: 비즈니스 로직과 API 클라이언트와의 상호 작용할 때 사용.
엔티티 클래스
- JPA 제공 애노테이션
| 애노테이션 | 설명 |
| --- | --- |
| @Entity | JPA 엔티티 클래스로 정의한다. |
| @Id | id 필드를 엔티티의 기본 키로 지정한다.
지정된 id 필드는 데이터베이스 테이블의 기본 키 컬럼과 매핑된다.
@Column 애노테이션 역할도 겸한다. |
| @GeneratedValue | id 필드가 자동 생성되는 값임을 나타난다.
strategy=GenerationType.IDENTITY : 기본 키 생성을 데이터베이스에 맡기는 전략. 주로 MySQL 같은 디비에서 AUTO_INCREMENT 속성으로 기본 키 생성하여 사용함. |
| @Column | 엔티티 클래스 필드를 데이터베이스 테이블의 열과 매핑 처리.
이름이 같은 경우에는 생략도 가능함. 명시적으로 하기 위해서 보통 붙임. |DTO 클래스
- RequestDto: 클라이언트 요청을 표현하는 DTO.
- ResponseDto: 서버 응답을 표현하는 DTO.
Mapper 클래스
- 엔티티와 DTO 로 분리했기 때문에 중간에 변환을 담당할 클래스가 필요하다.
- 매퍼 클래스는 이 역할을 수행한다.
리포지토리 레이어 - Repository → JpaRepository 인터페이스 확장하기
- JpaRepository 인터페이스: 스프링 데이터 JPA 에서 제공하는 인터페이스. 기본 CRUD 기능을 자동으로 제공한다.
- 두가지 타입의 매개 변수: 엔티티와 엔티티의 기본 키 타입.
- 스프링 데이터 JPA 가 자동으로 구현체를 생성해준다.
서비스 레이어 - 데이터베이스 연동에 대한 데이터 무결성 보장 처리
- @Transactional(readOnly = true): 읽기 전용 트랜잭션. 데이터 조회 메서드에 적용하는 편. 성능 최적화 및 데이터베이스 쓰기 잠금 방지. 읽기 작업 빠르게 수행.
- @Transactional: 기본 트랜잭션. 데이터베이스 추가, 수정, 삭제 작업 메서드에 적용하는 편. 변경 내용이 모두 적용 되거나 모두 취소 하도록 함. 저장 중 오류 발생 시 롤백 진행함.
REST 컨트롤러 - 의존성 주입
필드 의존성 주입은 추천하지 않음. 테스트가 어렵고 불변성 보장이 어렵다. 객체 순환 의존성을 발견하기 어렵다.
되도록 생성자 의존성 주입을 추천한다.
@Autowired private TodoService todoService; // 필드 의존성 주입. 추천하지 않음. //------------------------ // 생성자 의존성 주입. @Autowired public TodoService(TodoRepository todoRepository) { this.todoRepository = todoRepository; }
Testcontainers 사용한 서비스 테스트
@SpringBootTest
@Testcontainers
@ExtendWith(SpringExtension.class)
public class TodoServiceTests {
@Container
public static MySQLContainer<?> mysqlContainer =
new MySQLContainer<>("mysql:8.0.32")
.withDatabaseName("testdb")
.withUsername("test")
.withPassword("test");
@DynamicPropertySource
static void configureProperties(DynamicPropertyRegistry registry) {
registry.add("spring.datasource.url", mysqlContainer::getJdbcUrl);
registry.add("spring.datasource.username", mysqlContainer::getUsername);
registry.add("spring.datasource.password", mysqlContainer::getPassword);
}
@Autowired private TodoService todoService;
@Autowired private TodoRepository todoRepository;
private Long todo1Id;
private Long todo2Id;
@BeforeEach
void setUp() {
todoRepository.deleteAll();
todoService = new TodoService(todoRepository);
todo1Id = todoService.save(new TodoRequestDto("Test Todo 1", "Description 1")).getId();
todo2Id = todoService.save(new TodoRequestDto("Test Todo 2", "Description 2")).getId();
}
@Test
void testFindAll() throws Exception {
List<TodoResponseDto> todos = todoService.findAll();
assertThat(todos).hasSize(2);
}
@Test
void testSaveTodo() throws Exception {
TodoRequestDto todo = new TodoRequestDto("New Todo", "New Description");
todoService.save(todo);
assertThat(todoService.findAll()).hasSize(3);
}
@Test
void testFindById() throws Exception {
TodoResponseDto todo = todoService.findById(todo1Id);
assertThat(todo).isNotNull();
assertThat(todo.getTitle()).isEqualTo("Test Todo 1");
}
@Test
void testUpdateTodo() throws Exception {
TodoRequestDto updatedTodo =
new TodoRequestDto("Updated Todo", "Updated Description", true);
todoService.update(todo1Id, updatedTodo);
TodoResponseDto todo = todoService.findById(todo1Id);
assertThat(todo.getTitle()).isEqualTo("Updated Todo");
assertThat(todo.getDescription()).isEqualTo("Updated Description");
assertThat(todo.isCompleted()).isTrue();
}
@Test
void testDeleteTodo() throws Exception {
todoService.delete(todo1Id);
assertThat(todoService.findAll()).hasSize(1);
assertThat(todoService.findById(todo1Id)).isNull();
}
}
@Testcontainers
클래스 단위에서 Testcontainers 사용을 활성화하는 애노테이션.
해당 클래스 내에 선언된 @Container 필드를 인식하고 각 테스트에 대해 컨테이너 생명 주기를 관리한다.
@Testcontainers 가 없을 경우, Testcontainers는 컨테이너 필드를 자동으로 관리하지 않고, 개발자가 수동으로 컨테이너 시작과 종료를 처리해야 한다.
테스트 환경을 컨테이너화해서 처리한다. 실제 테스트 실행 시, 도커 인스턴스가 생성되고 실행된 후 테스트가 종료되면 삭제된다.
@ExtendWith(SpringExtension.class)
- JUnit 5 확장 기능 연결할 때 사용한다.
- 스프링 테스트 컨텍스트를 JUnit 5 테스트와 통합한다.
- 테스트 실행 시 스프링 애플리케이션 컨텍스트가 자동으로 생성 → 테스트 클래스에 @Autowired 로 의존성 주입할 수 있다.
- 트랜잭션 관리, 설정 빈 로딩 등 스프링이 제공하는 다양한 기능을 테스트 코드에 제공한다.
- 사용하지 않을 경우, 단순한 JUnit 테스트로만 동작한다. 스프링 컨텍스트가 없으므로 모두 수동으로 설정해야 한다.
@Container
- Testcontainers 에서 제공하는 애노테이션.
- 테스트 중에 사용할 컨테이너를 선언함.
- 데이터베이스 또는 컨테이너화 된 서비스를 테스트 환경에서 실행할 수 있다.
- 선언된 컨테이너는 테스트 실행 전에 자동으로 시작되고, 테스트가 끝나면 종료된다.
동적 속성 주입 DynamicPropertySource
@DynamicPropertySource: 테스트 실행 중 필요한 속성(properties)을 동적으로 주입한다.
@Container 로 지정한 컨테이너가 실행 될 때, 생성된 데이터베이스 URL, 사용자 이름과 비밀번호 등등 데이터베이스 연결에 필요한 정보를 스프링 애플리케이션 컨텍스트에 자동 주입한다.
컨테이너에서 생성된 데이터베이스 연결 정보가 동적으로 주입되어 JUnit5 테스트에서 해당 데이터베이스를 사용할 수 있다.
동적 속성 주입을 사용하지 않을 경우 아래와 같은 방법으로 해야 한다.
application-test.properties 파일에 데이터베이스 정보를 명시적으로 설정해야 한다.
# 디비 URL 설정. spring.datasource.url=jdbc:mysql://localhost:3306/test_db # 디비 사용자 이름 설정. spring.datasource.username=test # 디비 사용자 비밀번호 설정. spring.datasource.password=test
Test 실행
gradle test- PASSED 로 나타나면 정상.
실행 및 Swagger-UI 를 이용한 API 테스트
gradle bootRun- http://localhost:8080/swagger-ui/index.html
스프링 부트의 데이터베이스 연동
- JPA - 자바 진영에서 공식 채택된 표준 ORM 인터페이스. 스프링 부트는 JPA 연동을 가장 강력하게 지원. 실무에서 가장 많이 사용함.
- JPA 의 가장 큰 장점
- 생산성과 유지보수성.
- 기본 CRUD 작업 및 쿼리는 자동으로 처리 해줌.
- 스프링 부트와 결합이 잘 되어 있어 트랜잭션 관리, 캐싱, 데이터 검증 등 부가 기능까지 한 번에 활용할 수 있다.
- 단점
- 복잡한 쿼리나 대용량 데이터 처리가 필요할 경우, JPA 동작 원리를 깊이 이해하지 못한 경우에는 오히려 성능 이슈나 숨은 버그에 시달릴 수 있음.
- JPA 영속성 컨텍스트, 지연 로딩, 플러싱, 더티 체킹 등 내부 메커니즘이 복잡하기 때문에 예상치 못한 결과가 나올 수 있음.
- 데이터베이스 고유 기능이 중요한 프로젝트에서는 활용하는 데에 한계가 있음. 인덱스 튜닝이나 저장 프로시저 활용.
- 대안
- MyBatis 같은 프레임워크와 혼용해서 사용하는 경우가 많음.
- 아주 복잡한 쿼리나 성능이 중요한 영역에서는 MyBatis, QueryDSL, 또는 SQL 직접 작성 방식으로 보완한다.
- 으아 에디터를 인텔리제이 → 비주얼스튜디오 코드로 변경했더니 책 그대로 안 되네 ㅡ.ㅡ;;;;
- 다시 인텔리제이 아이디어로 변경. 그리고 구독했음!!
https://github.com/diffplug/spotless/tree/main/plugin-gradle#google-java-format
https://marketplace.visualstudio.com/items?itemName=JoseVSeb.google-java-format-for-vs-code ⇒ 이걸 설치하면 구글 자바 포맷이랑 맞춰짐.
추가 설정 : 설정 → 확장 → Google Java Format for VS Code → Java.Format.Settings.Google.Extra
"[java]": { "editor.defaultFormatter": "josevseb.google-java-format-for-vs-code" }, "java.format.settings.google.extra": "--aosp"
자바 포맷팅 옵션 일부 변경 → vs code 확장으로 설치한 것과 맞추기 위함.
// Spotless 플러그인을 사용하여 코드 양식을 자동으로 맞춰줍니다. spotless { // Java 파일에 대한 포맷팅 설정을 추가합니다. java { // Google Java Format 적용. // reflowLongStrings => + 연산자를 이용하여 읽기 좋게 변경함. googleJavaFormat('1.34.1').aosp().reflowLongStrings().reorderImports(true) } } // build 할 때 spotlessApply 를 실행하며 자동으로 코드의 양식을 맞춥니다. build.dependsOn 'spotlessApply'TestContainer 테스트 시 도커 연결 실패 현상 수정 건
오류 메시지
java.lang.IllegalStateException: Could not find a valid Docker environment. Please see logs and check configuration- TestContainers 와 Docker 사이의 API 버전 불일치로 인함.
해결
// src/main/resources/docker-java.properties -> 없다면 생성. api.version=1.44
메서드 이름 기반 쿼리 자동 생성
🤟소감 3줄 요약
- JPA 는 스펙이므로 따로 찾아봐야 하는 게 맞았다. 따로 공부할 정도로 양이 많다.
- 테스트 코드 구현 기술을 익히면 나중에 좋다.
- 스프링 부트는 자료가 있으나, 이를 취합하여 정리한 게 많이 없는 것 같다. 아쉽다.
'Do it 스터디! > 스프링 부트 개발자 온보딩 가이드 스터디!' 카테고리의 다른 글
| [스프링 부트 개발자 온보딩 가이드 스터디] 복잡한 실제 비즈니스 요구사항과 JPA 코드 구조 잡기 (0) | 2026.04.03 |
|---|---|
| [스프링 부트 개발자 온보딩 가이드 스터디] Chapter 05 고급 JPA 기반의 마이크로블로그 REST API 서버 개발 (0) | 2026.04.03 |
| [스프링 부트 개발자 온보딩 가이드 스터디] Chapter 03. 인메모리 기반의 To-Do 리스트 REST API 서버 만들기 (0) | 2026.04.03 |
| [스프링 부트 개발자 온보딩 가이드 스터디] Chapter 02 스프링 부트란 무엇인가요? (0) | 2026.04.02 |
| [스프링 부트 개발자 온보딩 가이드 스터디] Chapter 01 온보딩 가이드의 목적과 활용법 (0) | 2026.04.02 |
