JDBC 일정 관리 과제 (3)

ssongyi·2025년 3월 24일
0

필수기능

Lv 0. API 명세 및 ERD 작성

  • API 명세서 작성하기
    • API명세서는 프로젝트 root(최상위) 경로의 README.md 에 작성
  • ERD 작성하기
    • ERD는 프로젝트 root(최상위) 경로의 README.md 에 첨부
  • SQL 작성하기
    • 설치한 데이터베이스(Mysql)에 ERD를 따라 테이블을 생성

Lv 1. 일정 생성 및 조회

  • 일정 생성(일정 작성하기)

    • 일정 생성 시, 포함되어야할 데이터
      • 할일, 작성자명, 비밀번호, 작성/수정일을 저장
      • 작성/수정일은 날짜와 시간을 모두 포함한 형태
    • 각 일정의 고유 식별자(ID)를 자동으로 생성하여 관리
    • 최초 입력 시, 수정일은 작성일과 동일
  • 전체 일정 조회(등록된 일정 불러오기)

    • 다음 조건을 바탕으로 등록된 일정 목록을 전부 조회
      • 수정일 (형식 : YYYY-MM-DD)
      • 작성자명
    • 조건 중 한 가지만을 충족하거나, 둘 다 충족을 하지 않을 수도, 두 가지를 모두 충족할 수도 있습니다.
    • 수정일 기준 내림차순으로 정렬하여 조회
  • 선택 일정 조회(선택한 일정 정보 불러오기)

    • 선택한 일정 단건의 정보를 조회할 수 있습니다.
    • 일정의 고유 식별자(ID)를 사용하여 조회합니다.

    Lv 2. 일정 수정 및 삭제

  • 선택한 일정 수정

    • 선택한 일정 내용 중 할일, 작성자명 만 수정 가능
      • 서버에 일정 수정을 요청할 때 비밀번호를 함께 전달합니다.
      • 작성일은 변경할 수 없으며,수정일` 은 수정 완료 시, 수정한 시점으로 변경합니다.
  • 선택한 일정 삭제

    • 선택한 일정을 삭제할 수 있습니다.
      • 서버에 일정 수정을 요청할 때 비밀번호를 함께 전달합니다.

도전기능

Lv 3. 연관 관계 설정

  • 작성자와 일정의 연결
    • 설명
      • 동명이인의 작성자가 있어 어떤 작성자가 등록한 ‘할 일’인지 구별할 수 없음
      • 작성자를 할 일과 분리해서 관리합니다.
      • 작성자 테이블을 생성하고 일정 테이블에 FK를 생성해 연관관계를 설정해 봅니다.
    • 조건
      • 작성자 테이블은 이름 외에 이메일, 등록일, 수정일 정보를 가지고 있습니다.
        • 작성자의 정보는 추가로 받을 수 있습니다.(조건만 만족한다면 다른 데이터 추가 가능)
      • 작성자의 고유 식별자를 통해 일정이 검색이 될 수 있도록 전체 일정 조회 코드 수정.
      • 작성자의 고유 식별자가 일정 테이블의 외래키가 될 수 있도록 합니다.

Lv 4. 페이지네이션

  • ✅ 설명
    • 많은 양의 데이터를 효율적으로 표시하기 위해 데이터를 여러 페이지로 나눕니다.
      • 페이지 번호페이지 크기를 쿼리 파라미터로 전달하여 요청하는 항목을 나타냅니다.
      • 전달받은 페이지 번호와 크기를 기준으로 쿼리를 작성하여 필요한 데이터만을 조회하고 반환
  • 조건
    • ✅ 등록된 일정 목록을 페이지 번호크기를 기준으로 모두 조회
    • ✅ 조회한 일정 목록에는 작성자 이름이 포함
    • ✅ 범위를 넘어선 페이지를 요청하는 경우 빈 배열을 반환
    • Paging 객체를 활용할 수 있음

Lv 5. 예외 발생 처리

  • ✅ 설명

    • 예외 상황에 대한 처리를 위해 HTTP 상태 코드(링크)에러 메시지를 포함한 정보를 사용하여 예외를 관리할 수 있습니다.
      1. 필요에 따라 사용자 정의 예외 클래스를 생성하여 예외 처리를 수행할 수 있습니다.
      2. @ExceptionHandler를 활용하여 공통 예외 처리를 구현할 수도 있습니다.
      3. 예외가 발생할 경우 적절한 HTTP 상태 코드와 함께 사용자에게 메시지를 전달하여 상황을 관리합니다.
  • ✅ 조건

    • 수정, 삭제 시 요청할 때 보내는 비밀번호가 일치하지 않을 때 예외가 발생합니다.
    • 선택한 일정 정보를 조회할 수 없을 때 예외가 발생합니다.
      1. 잘못된 정보로 조회하려고 할 때
      2. 이미 삭제된 정보를 조회하려고 할 때

Lv 6. null 체크 및 특정 패턴에 대한 검증 수행

  • ✅ 설명
    • 유효성 검사
      1. 잘못된 입력이나 요청을 미리 방지할 수 있습니다.
      2. 데이터의 무결성을 보장하고 애플리케이션의 예측 가능성을 높여줍니다.
      3. Spring에서 제공하는 @Valid 어노테이션을 이용할 수 있습니다.
  • ✅ 조건
    • 할일은 최대 200자 이내로 제한, 필수값 처리
    • 비밀번호는 필수값 처리
    • 담당자의 이메일 정보가 형식에 맞는지 확인

스케줄 관리 API 명세서


SimpleJdbcInsert 의 자동매핑으로 인한 null 에러

작성일을 내가 직접 수기로 입력하지 않더라도 자동으로 배치시키기 위해 DEFAULT 를 사용했다.
또한 NULL 값이면 안된다고 생각해서 NOT NULL 을 같이 사용했다.

그랬더니 api 를 돌렸을때 500 에러 ( Column 'created_at' cannot be null ) 가 발생했다.

@Override
public Schedule save(Schedule schedule) {
  // INSERT Query: 문자열을 직접 작성하지 않아도 됨
 SimpleJdbcInsert jdbcInsert = new SimpleJdbcInsert(jdbcTemplate);

 // schedule 이라는 테이블에 Insert 하겠다 + 이 테이블의 key 값은 id 라는 이름으로 설정되어 있다.
 jdbcInsert.withTableName("schedule").usingGeneratedKeyColumns("schedule_id");

문제 원인

  • SimpleJdbcInsert가 Java 객체에서 만든 필드명을 기준으로 자동으로 컬럼을 추론해서 insert 하려고 할 때,
    모든 필드를 매핑 대상으로 포함시키는 것
  • 나는 parameters 를 직접 넣었지만,
    아직도 SimpleJdbcInsert는 내부적으로 columnMapping을 추론할 수 있다.
  • 그러면 created_at = null 이 다시 포함될 수 있음.

MySQL의 DEFAULT CURRENT_TIMESTAMP는 컬럼을 아예 INSERT에서 생략할 때만 동작한다.

그런데 내 코드는 SimpleJdbcInsert가 자동으로 모든 필드를 매핑할 때 createdAt = null 값이 부여되었다.

따라서 DEFAULT가 무시되고, null이 그대로 들어가면서 에러가 나는 것이었다.

해결방안

" SimpleJdbcInsert에 insert할 컬럼을 명시적으로 지정하기 "

SimpleJdbcInsert jdbcInsert = new SimpleJdbcInsert(jdbcTemplate)
        .withTableName("schedules")
        .usingGeneratedKeyColumns("schedule_id")
        .usingColumns("schedule_title", "content", "schedule_password", "member_id"); // 👈 여기 명시!

이렇게 하면, created_at은 명시적으로 제외되기 때문에
DB에서 DEFAULT CURRENT_TIMESTAMP가 정상적으로 동작하게 된다.

왜 이렇게 하는가 ?

SimpleJdbcInsertusingColumns()을 지정하지 않으면
Map의 모든 key와 데이터베이스 테이블의 모든 컬럼을 비교해서 자동 매핑하려고 시도함.
→ 이 과정에서 created_at = null 이 들어가 버리는 경우가 생김


필요한 것만 DTO에서 받아오기(setter)

Scehdule 클래스에서 생성자를 만들었다.

updated_at 이 없어서 Expected 1 argument but found 5 에러가 나고 있다.

너무너무 비효율적이라고 생각했다.

쿼리에서 가져온 데이터는 많고,
그걸 전부 ScheduleResponseDto 생성자에 맞춰서 전부 다 써야한다니?
그래서 필요한것만 쓰고 싶어서 아래와 같이 해결했다.

필드가 많고 일부만 필요하여,
생성자 주입 말고 setter 방식으로 구현했다.


NoArgsConstructor 은 권장되는 어노테이션일까?

앞선 과정에서 Schedule 에 Expected 7 arguments but found 0 에러가 났다.

Schedlue 클래스에 기본생성자(매개변수 없는 생성자) 가 없어서 생기는 에러로 판단하여
NoArgsConstructor 을 추가하여 Lombok 이 자동으로 기본 생성자를 만들어주게끔 했다.

에러는 일단락시켰지만.... 조금 걱정이 됐다.
NoArgsConstructor 은 매개변수를 체크해주지 않아서 위험할 것 같은데, 권장되는 어노테이션일까?

NoArgsConstructor 은 조사한 바로는,
DTO, Entity, ORM 매핑 객체에서는 꽤 자주 쓰이는 어노테이션이지만,
도메인 로직을 담은 객체나, 무조건 필드가 필수인 객체에서는 유의해야 한다고 한다.

나와 같은 경우는...
DB 에서 매핑해주는 용도로만 사용하기 때문에 적절한 사용이라고 생각한다.


과제 피드백

과제 완성도

과제 이해도

과제 평가

피드백 정리

  1. entity객체 업데이트와 DB 업데이트를 별개의 stream으로 만들어서 스프링에는 반영이 되었으나, DB에 반영이 안될 수 도 있음.
    --> @Transactional을 사용해 같은 작업이라는걸 명시해 주기

  2. ScheduleRepository 인터페이스에는 @Repository 을 넣을 필요 X
    --> 구현체에만 적용하기

  3. MemberUpdate시 비밀번호 확인을 바로 해주고 있는데, 만약 dbPassword가 없다면 NullPointerException이 발생함
    --> DB에서 값을 꺼낼때는 언제나 Optional을 활용해 주기

0개의 댓글