일정 관리 앱 만들기(Spring JPA 사용)

2025. 4. 6. 22:57·TIL

파일 구조

📦 com.example.schedule
│
├── controller
│   ├── HomeController      --> 🧑 HTML 뷰 렌더링 (홈, 회원가입, 로그인, 일정 등록, 일정 목록)
│   ├── UserController       --> 📦 회원가입, 유저 수정 컨트롤러
│   ├── AuthController       --> 로그인/로그아웃(인증 관련) 컨트롤러

│   ├── CommentController --> 댓글 관련 컨트롤러

│   ├── ApiUserController   --> 📦 REST API (유저 관련 JSON 처리)

│   ├── ScheduleController   --> 📦 REST API (일정 관련 JSON 처리)
│
├── dto
│   ├── UserRequestDto          --> 회원가입/로그인 요청 DTO
│   ├── UserResponseDto         --> 유저 응답 DTO (API용)
│   └── Schedule DTO들 ...
│
├── domain
│   ├── User                    --> 회원 Entity
│   ├── Schedule                --> 일정 Entity
│   └── BaseTimeEntity          --> 생성/수정일 공통 Entity
│
├── service
│   ├── UserService             --> 회원 비즈니스 인터페이스
│   ├── UserServiceImpl         --> 회원 비즈니스 구현체
│   ├── ScheduleService         --> 일정 인터페이스
│   └── ScheduleServiceImpl     --> 일정 구현체
│
├── repository
│   ├── UserRepository
│   └── ScheduleRepository
│
├── config
│   ├── WebConfig               --> 필터 등록 등 설정
│   └── AuthFilter              --> 인증 필터 (세션 검사)
│
├── exception
│   ├── CustomException
│   ├── ErrorCode
│   └── GlobalExceptionHandler
│
└── templates
    ├── home.html               --> 홈 페이지
    ├── login.html              --> 로그인 폼
    ├── register.html           --> 회원가입 폼
    └── schedule/               --> 일정 관련 페이지들

1. 엔티티 구현

  • User, Schedule 엔티티 정의
  • 공통된 시간 필드는 BaseTime 엔티티로 구현해서 분리
  • ( 할 일 같은 경우엔 작성자, 수정자도 언젠간 필요할 듯 하여 Base 엔티티 상속 )
  • JPA Auditing 설정 
  • 연관관계는 단방향, (Schedule : N -> User : 1, 연관관계의 주인은 FK를 가진 곳)

Schedule 엔티티

@Entity
@Getter
@NoArgsConstructor(access = AccessLevel.PROTECTED)
public class Schedule extends BaseTime {
    @Id @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "schedule_id")
    private Long id;

    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "user_id")
    private User user; 

    @Column(nullable = false)
    private String title;

    @Column(nullable = false)
    private String content;

    // 생성자 
    private Schedule(User user, String title, String content) {
        this.user = user;
        this.title = title;
        this.content = content;
    }
    // 정적 팩토리 메서드
    public static Schedule of(User user, String title, String content) {
        return new Schedule(user, title, content);
    }
  }

 

@NoArgsConstructor(access = AccessLevel.PROTECTED)

  • 스프링부트에선 @Builder나 생성자를 따로 설정해주는데, @NoArgsConstructor은 또 뭘까?
  • AccessLevel은 왜 PROTECTED일까?

@NoArgsConstructor는 기본 생성자를 뜻하는 것이고, 해당 생성자의 access level을 protected로 설정한다는 뜻이다.

그럼 엔티티에서 왜 이 어노테이션을 사용할까? 

 

결론부터 말하면 엔티티의 Proxy 조회 때문이다.

엔티티의 연관 관계가 맺어져있는 객체를 조회할 때 지연 로딩의 경우엔 실제 엔티티가 아닌 객체의 참조값을 가지는 프록시 객체를 조회 한 후, 해당 객체 데이터를 호출했을 때 프록시 객체가 해당 객체의 참조값을 통해 조회한다.

 

...?

아직까지는 이해하기 어렵다.. 나중에 자세히 공부를 해봐야 할 것 같다.

 

@ManyToOne(fetch = FetchType.LAZY) 

연관 관계에서 즉시 로딩과 지연 로딩

즉시로딩은 JPQL에서 N+1 문제를 일으켜 실무에서 쓰지 않는다고 하였고 발생하는 오류 중에서 즉시 로딩 때문에 문제가 있었다~ 라는 걸 강의에서 봐서 일단은 지연 로딩을 사용했다. 이 부분에 대해서도 자세히 공부해봐야 할 것 같다.

 

정적 팩토리 메서드(Static Factory Method)

객체를 생성할 때, 생성자를 쓰지 않고 정적 메서드를 사용하는 것이다. 생성자의 접근 제어자가 public인 경우, 생성자를 통해 객체 생성을 언제 어디서든 제한없이 할 수 있게 되고, 어떤 인스턴스를 반환한 것인지 제어할 수 없다.

정적 팩토리 메서드를 사용하면 객체 생성을 자기 자신이 관리할 수 있다.

@Builder가 JPA 지연 로딩이나 기본 생성자와 충돌날 수 있어 정적 팩토리 메서드를 사용했는데 솔직히 말하면 아직 이해가 안가는 부분이 많다.

 

싱글톤 패턴, 지연 로딩, reflection 등등 다양한 키워드가 나와서 다음주에 시간내고 쫙 공부해야겠다.. 이해가 안가는 것 투성이다.

 

사실 이번에 테스트 코드도 조금 작성해봤다. JPA를 처음 사용해본 입장으로써 나는 SQL 을 작성하지 않았는데 JPA 내부에서 

Entity를 사용하면 매핑이 되는 걸까? 이해가 안가서 어떻게 동작하는지 알고 싶었다.

 

따라서 Entity 단위로 테스트하는 테스트 코드를 작성 후 테스트를 해봤다.

@PersistenceContext
EntityManager em;

@Test
public void testEntity() {
    User user1 = User.of("admin", "admsadadasin", "admsain@gmail.com");
    em.persist(user1);

    Schedule schedule1 = Schedule.of(user1, "회의", "회의를 합니다" );
    em.persist(schedule1);

    em.flush();
    em.clear(); //1차 캐시 제거,

    Schedule sc = em.find(Schedule.class, schedule1.getId());
    String username = user1.getUsername();
    assertThat(username).isEqualTo(user1.getUsername());
}
정리
설정  이유
@Transactional 트랜잭션 유지 → LAZY 로딩 가능
em.persist(...) EntityManager를 통해 직접 저장, "이 엔티티를 저장할거야" 라고 JPA에게 알려줌
em.flush() DB에 강제로 반영, "지금까지 등록된 변경 내용을 진짜 DB에 저장해"
em.clear() 1차 캐시 제거 → 영속성 컨텍스트를 초기화해!

...

 

 

'TIL' 카테고리의 다른 글

TIL21 - 뉴스피드 프로젝트(엔티티 생성, 연관관계 설정), 작성중......  (2) 2025.04.09
TIL20 - 뉴스피드 프로젝트(깃 컨벤션, 이슈, 브랜치)  (4) 2025.04.09
📅 일정 관리 애플리케이션(도전과제3)  (0) 2025.03.25
📅 일정 관리 애플리케이션(필수과제)  (0) 2025.03.23
TIL13. 키오스크 도전과제2  (1) 2025.03.14
'TIL' 카테고리의 다른 글
  • TIL21 - 뉴스피드 프로젝트(엔티티 생성, 연관관계 설정), 작성중......
  • TIL20 - 뉴스피드 프로젝트(깃 컨벤션, 이슈, 브랜치)
  • 📅 일정 관리 애플리케이션(도전과제3)
  • 📅 일정 관리 애플리케이션(필수과제)
에그마요샌드위츼
에그마요샌드위츼
  • 에그마요샌드위츼
    아자아자
    에그마요샌드위츼
  • 전체
    오늘
    어제
    • 분류 전체보기
      • java
      • sql
      • TIL
      • spring
      • JPA
      • spring-security
  • 블로그 메뉴

    • 홈
    • 태그
    • 방명록
  • 링크

  • 공지사항

  • 인기 글

  • 태그

    GIT
    til
  • 최근 댓글

  • 최근 글

  • hELLO· Designed By정상우.v4.10.3
에그마요샌드위츼
일정 관리 앱 만들기(Spring JPA 사용)
상단으로

티스토리툴바