Loading the catalog…
Loading the catalog…
1. 3 Layer Architecture 기존 메모장 프로젝트에서는 하나의 Controller에서 API 요청부터 DB 작업까지 모두 처리하고 있었다. 기능이 많아질수록 한 클래스에 코드가 몰리게 되고, 수정이나 유지보수가 어려워진다. 이를 해결하기 위해 역할을 Controller → Service → Repository 로 분리한다. Controller 클라이언트의 요청을 받는 역할 HTTP 요청 처리 Request 데이터 전달 Service 호출 처리 결과를 Client에게 응답 Service 비즈니스 로직 을 담당 사용자의 요구사항 처리 비즈니스 로직 수행 DB 작업이 필요하면 Repository에 요청 Repository DB와 직접적으로 통신하는 역할 DB 연결 및 관리 CRUD 작업 SQL 실행 전체 흐름 Client ↓ Controller ↓ Service ↓ Repository ↓ Database 각 계층의 역할을 분리하면 코드의 가독성, 유지보수성, 확장성 을 높일 수 있다. 2. IoC와 DI 의존성 한 객체가 다른 객체를 필요로 하는 관계를 의미한다. 예를 들어 Consumer 가 Chicken 을 직접 생성해서 사용한다면 두 객체가 강하게 결합되어 있다. public class Consumer { void eat() { Chicken chicken = new Chicken(); chicken.eat(); } } 이 상태에서는 Chicken 을 Pizza 로 변경하려면 Consumer 의 코드까지 수정 필요 약한 결합 인터페이스를 사용하면 객체 간 결합도를 낮출 수 있다. interface Food { void eat(); } Consumer 는 Chicken 이나 Pizza 자체가 아니라 Food 에 의존한다. Consumer → Food ↑ Chicken / Pizza 따라서 실제 구현체가 변경되어도 Consumer 의 코드를 크게 수정하지 않아도 된다. DI (Dependency Injection) 필요한 객체를 외부에서 주입받는 것 객체가 필요한 의존성을 직접 생성하지 않고 외부에서 전달받는다. 대표적인 방법은 생성자 주입 이다. public class Consumer { private final Food food; public Consumer(Food food) { this.food = food; } } 생성자 주입의 장점 객체 간 결합도 감소 의존성 관리 용이 테스트 용이 객체의 불변성 확보 Spring에서는 일반적으로 생성자 주입을 권장 한다. IoC (Inversion of Control) 제어의 역전 객체의 생성과 의존성 관리에 대한 제어권을 개발자가 직접 갖는 것이 아니라 Spring이 관리하는 구조 이다. 기존에는 Controller → Service 생성 Service → Repository 생성 과 같이 객체를 직접 생성했다. DI를 적용하면 Repository → Service → Controller 형태로 필요한 객체를 외부에서 주입받는다. 즉, 객체의 생성과 의존성 관리에 대한 제어가 역전된다. 3. IoC Container와 Bean Bean Spring이 생성하고 관리하는 객체 Spring은 애플리케이션 실행 시 필요한 객체를 생성하고 IoC Container에서 관리한다. IoC Container Spring Bean을 생성하고 관리하는 컨테이너 Spring IoC Container ├─ Controller Bean ├─ Service Bean └─ Repository Bean @Component 클래스를 Spring Bean으로 등록할 때 사용한다. @Component public class MemoService { } @ComponentScan @Component 가 붙은 클래스를 찾아 Bean으로 등록한다. @SpringBootApplication 에 기본적으로 설정되어 있다. 4. Spring의 3 Layer Annotation Spring에서는 각 계층의 역할을 나타내는 애너테이션을 제공한다. Annotation 역할 @Controller Controller 역할 @RestController REST API Controller @Service Service 역할 @Repository Repository 역할 이 애너테이션들은 모두 @Component 를 포함하고 있기 때문에 Spring Bean으로 등록된다. 5. JPA란? ORM Object-Relational Mapping 객체와 관계형 데이터베이스의 데이터를 매핑하는 기술이다. 기존에는 DB 작업을 위해 직접 SQL을 작성하고 JDBC를 사용해야 했다. Java 객체 ↓ SQL 작성 ↓ JDBC 실행 ↓ Database 데이터 구조가 변경되면 SQL과 객체 변환 코드도 함께 수정해야 하는 문제가 있다. JPA Java Persistence API Java ORM 기술에 대한 표준 명세 JPA를 사용하면 객체를 중심으로 DB 데이터를 다룰 수 있다. Java 객체 ↓ JPA ↓ Database 개발자가 직접 SQL을 작성하는 작업을 줄이고 객체 중심으로 DB를 관리할 수 있다. Hibernate JPA의 구현체 중 하나 Spring Boot에서는 기본적으로 Hibernate를 사용한다. JPA = 표준 명세 Hibernate = JPA 구현체 6. Entity JPA가 관리하는 객체 Entity 클래스는 DB 테이블과 매핑된다. @Entity @Table(name = "memo") public class Memo { @Id private Long id; @Column(nullable = false) private String username; @Column(nullable = false, length = 500) private String contents; } 주요 Annotation Annotation 역할 @Entity JPA가 관리하는 Entity로 지정 @Table 매핑할 테이블 지정 @Column 필드와 컬럼 매핑 @Id 기본 키 지정 @GeneratedValue 기본 키 자동 생성 @GeneratedValue @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; DB의 AUTO_INCREMENT 와 같이 기본 키 생성을 DB에 위임할 수 있다. 7. 영속성 컨텍스트 Entity 객체를 효율적으로 관리하기 위한 공간 JPA는 Entity를 바로 DB에 저장하는 것이 아니라 영속성 컨텍스트에서 관리 한다. EntityManager 영속성 컨텍스트에 접근하여 Entity를 관리하는 객체 EntityManager ↓ 영속성 컨텍스트 ↓ Entity Entity의 저장, 조회, 수정, 삭제 등을 관리한다. 8. 영속성 컨텍스트의 주요 기능 1차 캐시 영속성 컨텍스트 내부에는 Entity를 저장하는 캐시가 존재한다. 1차 캐시 Key → Value @Id Entity em.find() 를 호출했을 때 먼저 1차 캐시를 확인한다. em.find() ↓ 1차 캐시 확인 ↓ 있음 → 캐시에서 반환 ↓ 없음 → DB 조회 → 1차 캐시 저장 → 반환 1차 캐시의 장점 DB 조회 횟수 감소 같은 Entity에 대한 객체 동일성 보장 쓰기 지연 JPA는 INSERT , UPDATE , DELETE SQL을 바로 DB에 보내지 않고 쓰기 지연 저장소 에 모아둘 수 있다. 트랜잭션이 커밋되거나 flush() 가 호출되면 SQL을 DB에 반영한다. Entity 변경 ↓ 쓰기 지연 저장소 ↓ flush() ↓ DB flush() 영속성 컨텍스트의 변경 내용을 DB에 반영 쓰기 지연 저장소에 있는 SQL을 DB에 전달한다. 단, INSERT , UPDATE , DELETE 와 같은 변경 작업을 DB에 반영하려면 트랜잭션이 필요하다. 변경 감지 (Dirty Checking) JPA에서는 별도의 update() 메서드를 호출하지 않아도 Entity의 변경 내용을 감지하여 UPDATE SQL을 생성한다. Entity 조회 ↓ 최초 상태 저장 ↓ Entity 값 변경 ↓ 트랜잭션 commit ↓ flush() ↓ 최초 상태와 현재 상태 비교 ↓ 변경된 내용이 있다면 UPDATE SQL 생성 따라서 다음과 같이 Entity의 값만 변경해도 된다. Memo memo = em.find(Memo.class, id); memo.setUsername("Update"); memo.setContents("변경된 내용"); 이후 트랜잭션이 커밋되면 변경 사항이 DB에 반영된다. 9. Entity의 상태 JPA에서 Entity는 영속성 컨텍스트와의 관계에 따라 상태가 달라진다. 비영속 (Transient) Memo memo = new Memo(); 아직 영속성 컨텍스트에서 관리하지 않는 상태 영속 (Managed) em.persist(memo); 영속성 컨텍스트가 Entity를 관리하는 상태 준영속 (Detached) em.detach(memo); 영속 상태였던 Entity를 영속성 컨텍스트에서 분리한 상태 준영속 상태에서는 JPA의 변경 감지 등의 기능을 사용할 수 없다. 삭제 (Removed) em.remove(memo); Entity를 삭제 대상으로 지정한 상태 상태 정리 비영속 ↓ persist() 영속 ↓ detach() 준영속 영속 ↓ remove() 삭제 10. 영속성 컨텍스트 관리 메서드 persist() 비영속 Entity를 영속 상태로 만든다. em.persist(memo); find() Entity를 조회한다. em.find(Memo.class, id); detach() 특정 Entity를 영속성 컨텍스트에서 분리한다. em.detach(memo); clear() 영속성 컨텍스트를 초기화한다. em.clear(); 관리하고 있던 모든 Entity가 준영속 상태가 된다. close() 영속성 컨텍스트를 종료한다. em.close(); merge() 준영속 또는 비영속 Entity를 다시 영속 상태로 병합한다. Memo mergedMemo = em.merge(memo); merge() 의 반환값이 새롭게 영속 상태가 된 Entity라는 점이 중요하다. 11. Transaction 여러 DB 작업을 하나의 작업 단위로 묶는 개념 모든 작업이 성공하면 Commit , 하나라도 실패하면 Rollback 한다. Transaction 시작 ↓ DB 작업 ↓ 성공 → COMMIT 실패 → ROLLBACK @Transactional Spring에서는 @Transactional 을 사용하여 트랜잭션을 쉽게 적용할 수 있다. @Transactional public void updateMemo() { // DB 작업 } 정상적으로 실행되면 Commit하고, 예외가 발생하면 Rollback한다. readOnly = true 조회 전용 트랜잭션에서 사용한다. @Transactional(readOnly = true) 조회 작업에 대한 최적화가 가능하다. 12. 영속성 컨텍스트와 Transaction Spring 환경에서는 트랜잭션과 영속성 컨텍스트의 생명주기 가 연결되어 있다. 트랜잭션이 유지되는 동안 영속성 컨텍스트도 유지되기 때문에 JPA의 기능을 사용할 수 있다. 따라서 JPA에서 DB에 데이터를 저장, 수정, 삭제 하려면 트랜잭션 적용이 필요하다. 조회는 단순 조회라면 트랜잭션이 필수는 아니지만, 상황에 따라 트랜잭션이 필요한 경우가 있다. 13. Transaction 전파 Spring에서는 Service에서 Repository까지 트랜잭션을 전달할 수 있도록 트랜잭션 전파 기능을 제공한다. @Transactional 의 기본 전파 옵션은 REQUIRED 이다. REQUIRED 부모 메서드에 트랜잭션이 존재하면 자식 메서드가 부모 트랜잭션에 참여 한다. Service @Transactional ↓ Repository @Transactional → 하나의 Transaction으로 동작 즉, 여러 계층에서 @Transactional 이 사용되더라도 기존 트랜잭션이 있다면 새로운 트랜잭션을 만드는 것이 아니라 기존 트랜잭션에 합류한다. 14. Spring Boot에서 JPA 사용 Spring Boot에서는 JPA 설정을 보다 쉽게 할 수 있다. 의존성 implementation 'org.springframework.boot:spring-boot-starter-data-jpa' application.properties spring.jpa.hibernate.ddl-auto=update spring.jpa.properties.hibernate.show_sql=true spring.jpa.properties.hibernate.format_sql=true spring.jpa.properties.hibernate.use_sql_comments=true ddl-auto 옵션 역할 create 기존 테이블 삭제 후 생성 create-drop 생성 후 애플리케이션 종료 시 삭제 update 변경된 부분만 반영 validate Entity와 테이블 매핑 검증 none 아무 작업도 하지 않음 개발 환경에서는 설정에 따라 편리하게 사용할 수 있지만, 운영 환경에서는 데이터 손실 가능성을 고려해야 한다. 15. Spring Data JPA JPA를 쉽게 사용할 수 있도록 만들어진 Spring Data의 모듈 JPA를 직접 사용하면 EntityManager 등을 이용해 DB 작업을 처리해야 한다. Spring Data JPA에서는 JpaRepository 를 제공하여 반복적인 CRUD 코드를 줄일 수 있다. public interface MemoRepository extends JpaRepository<Memo, Long> { } Spring Data JPA가 Repository의 구현체를 자동으로 생성하기 때문에 직접 구현 클래스를 작성할 필요가 없다. 16. JpaRepository 주요 메서드 save() Entity 저장 memoRepository.save(memo); findAll() 전체 데이터 조회 memoRepository.findAll(); findById() ID를 기준으로 조회 memoRepository.findById(id); 반환 타입은 Optional 이다. memoRepository.findById(id) .orElseThrow(() -> new IllegalArgumentException("메모가 존재하지 않습니다.") ); delete() Entity 삭제 memoRepository.delete(memo); 17. 변경 감지를 이용한 수정 Spring Data JPA에는 별도의 update() 메서드가 없다. Entity를 조회한 후 값을 변경하면 변경 감지(Dirty Checking) 를 통해 UPDATE SQL이 생성된다. @Transactional public Long updateMemo(Long id, MemoRequestDto requestDto) { Memo memo = findMemo(id); memo.update(requestDto); return id; } 여기서 중요한 것은 @Transactional 이다. 트랜잭션이 종료될 때 변경 감지가 수행되고 DB에 UPDATE가 반영된다. 18. JPA Auditing 생성 시간이나 수정 시간을 Entity마다 직접 관리하는 것은 번거롭다. Spring Data JPA에서는 JPA Auditing 을 통해 생성일과 수정일을 자동으로 관리할 수 있다. @CreatedDate Entity가 처음 생성될 때 시간을 자동으로 저장한다. @LastModifiedDate Entity가 수정될 때 수정 시간을 자동으로 변경한다. @MappedSuperclass 공통으로 사용하는 필드를 여러 Entity가 상속받을 수 있도록 한다. @Getter @MappedSuperclass @EntityListeners(AuditingEntityListener.class) public abstract class Timestamped { @CreatedDate @Column(updatable = false) private LocalDateTime createdAt; @LastModifiedDate private LocalDateTime modifiedAt; } 그리고 Spring Boot 애플리케이션에 다음 설정을 추가한다. @EnableJpaAuditing Entity에서 상속받아 사용한다. public class Memo extends Timestamped { } 19. Query Methods Spring Data JPA에서는 메서드 이름을 이용하여 SQL을 생성 할 수 있다. 예를 들어 List<Memo> findAllByOrderByModifiedAtDesc(); 위 메서드는 modifiedAt 을 기준으로 내림차순 조회한다. 또한 조건을 메서드 이름에 표현할 수 있다. List<Memo> findAllByContentsContainsOrderByModifiedAtDesc( String keyword ); → contents 에 특정 keyword 가 포함된 데이터를 조회하고 수정일 기준으로 내림차순 정렬 Query Methods의 장점 SQL 직접 작성 감소 Repository 구현 코드 감소 메서드 이름만으로 조회 조건 표현 가능 20. 이번 주 핵심 정리 이번 주에 배운 내용을 흐름으로 정리하면 다음과 같다. 3 Layer Architecture ↓ Controller / Service / Repository 역할 분리 ↓ IoC / DI ↓ Spring IoC Container ↓ Bean 관리 ↓ JPA ↓ Entity ↓ 영속성 컨텍스트 ↓ 1차 캐시 / 쓰기 지연 / 변경 감지 ↓ Transaction ↓ Spring Data JPA ↓ JpaRepository ↓ Query Methods 꼭 기억할 키워드 3 Layer Architecture Controller Service Repository IoC 제어의 역전 DI 의존성 주입 생성자 주입 권장 Bean Spring이 관리하는 객체 JPA Java ORM 표준 명세 Hibernate JPA 구현체 Entity JPA가 관리하는 객체 영속성 컨텍스트 Entity를 관리하는 공간 1차 캐시 Entity 조회 성능 및 객체 동일성 보장 Dirty Checking Entity 변경 감지 → UPDATE SQL 생성 flush 영속성 컨텍스트의 변경 내용을 DB에 반영 Transaction Commit / Rollback @Transactional Spring에서 트랜잭션 적용 Transaction Propagation REQUIRED 가 기본값 기존 트랜잭션에 참여 Spring Data JPA JPA 사용을 편리하게 해주는 모듈 JpaRepository 기본 CRUD 기능 제공 JPA Auditing 생성일 / 수정일 자동 관리 Query Methods 메서드 이름으로 조회 조건 생성 마무리 이번 주에는 단순히 JPA 사용법만 배운 것이 아니라 Spring이 객체와 DB를 어떻게 관리하는지 를 중심으로 학습했다. 특히 IoC → DI → Bean → JPA → 영속성 컨텍스트 → Transaction → Spring Data JPA 가 서로 연결되어 있다는 점을 이해하는 것이 중요했다. 기존에는 Repository에서 직접 SQL을 작성했다면, Spring Data JPA를 사용하면서는 JpaRepository 와 Query Methods를 통해 훨씬 적은 코드로 DB 작업을 처리할 수 있다. 다음에는 이번에 배운 내용을 실제 프로젝트에 적용하면서 Entity 설계와 Repository, Service 계층을 직접 구성해보는 것 이 중요할 것 같다.
What RADAR observed and classified to build this opportunity. It is what the source published, not a verification that the offer is still active.
[Spring/입문]. 1. 3 Layer Architecture 기존 메모장 프로젝트에서는 하나의 Controller에서 API 요청부터 DB 작업까지 모두 처리하고 있었다. 기능이 많아질수록 한 클래스에 코드가 몰리게 되고, 수정이나 유지보수가 어려워진다. 이를 해결하기 위해 역할을 Controller → Service → Repository 로 분리한다. Controller 클라이언트의 요청을 받는 역할 HTTP 요청 처리 Request 데이터 전달 Service 호출 처리 결과를 Client에게 응답 Service 비즈니스 로직 을 담당 사용자의 요구사항 처리 비즈니스 로직 수행 DB 작업이 필요하면 Repository에 요청 Repository DB와 직접적으로 통신하는 역할 DB 연결 및 관리…
Open source