TIL

[250320 TIL] 일정 관리 웹 과제

도원좀비 2025. 3. 20. 21:28

프로젝트 개요

Spring Boot 3.4.3과 JDBC를 활용하여 일정 관리 웹 서비스를 개발했습니다.
CRUD 및 필터링 기능이 포함된 일정 관리 시스템으로,
사용자가 일정을 등록하고 수정, 삭제할 수 있도록 설계


1️⃣ 기술 스택

백엔드

  • Java 21: 최신 Java 기능 활용
  • Spring Boot 3.4.3: 경량화된 애플리케이션 개발
  • Spring Web: RESTful API 개발
  • Spring Data JDBC: MySQL과의 데이터 연동
  • Validation: 입력값 검증
  • Lombok: 코드 간결화

데이터베이스

  • MySQL
    • 일정(schedule) 및 작성자(author) 테이블 설계
    • 외래 키(FK) 관계 설정 (작성자와 일정 연관)
    • ON DELETE CASCADE 적용 (작성자 삭제 시 일정도 삭제)

빌드 및 배포

  • Gradle: 의존성 및 빌드 관리
  • GitHub: 버전 관리
  • Postman: API 테스트

2️⃣ 테이블 설계

📌 schedule (일정)

컬럼 명 타입 설명
schedule_id BIGINT 일정 ID (PK)
title VARCHAR(255) 일정 제목
content VARCHAR(2000) 일정 내용
create_date TIMESTAMP 생성 날짜 (기본값: 현재 시간)
updated_date TIMESTAMP 수정 날짜 (자동 업데이트)
author_id BIGINT 작성자 ID (FK)
password VARCHAR(255) 일정 삭제 및 수정 시 필요한 비밀번호

📌 author (작성자)

컬럼명 타입 설명
author_id BIGINT 작성자 ID (PK)
name VARCHAR(100) 작성자 이름
email VARCHAR(255) 이메일 (유니크)
created_date TIMESTAMP 생성 날짜 (기본값: 현재 시간)
updated_date TIMESTAMP 수정 날짜 (자동 업데이트)

 


3️⃣ 기능 구현

✅ 일정 등록 (Create)

  • SimpleJdbcInsert를 활용하여 일정 저장
  • author_id가 존재하는지 검증 후 등록
  • create_date, updated_date는 현재 시간으로 설정
더보기
@Override
public ScheduleResponseDto saveSchedule(Schedule schedule) {
    SimpleJdbcInsert jdbcInsert = new SimpleJdbcInsert(jdbcTemplate);
    jdbcInsert.withTableName("schedule").usingGeneratedKeyColumns("schedule_id");

    Map<String, Object> parameters = new HashMap<>();
    parameters.put("title", schedule.getTitle());
    parameters.put("content", schedule.getContent());
    parameters.put("author_id", schedule.getAuthorId());
    parameters.put("password", schedule.getPassword());
    parameters.put("create_date", schedule.getCreateDate());
    parameters.put("updated_date", schedule.getUpdatedDate());

    Number key = jdbcInsert.executeAndReturnKey(parameters);
    Long generatedId = key.longValue();

    return new ScheduleResponseDto(
            generatedId,
            schedule.getTitle(),
            schedule.getContent(),
            schedule.getUpdatedDate().toString(),
            schedule.getAuthorId()
    );
}

✅ 일정 목록 조회 (Read)

  • JOIN을 활용하여 작성자 이름과 함께 일정 조회
  • updated_date 기준 내림차순 정렬
더보기
@Override
public List<ScheduleAuthorDto> findAllSchedule() {
    return jdbcTemplate.query(
        "SELECT s.title, s.content, s.updated_date, a.name " +
        "FROM schedule s " +
        "JOIN author a ON s.author_id = a.author_id " +
        "ORDER BY s.updated_date DESC",
        scheduleAuthorRowMapper()
    );
}
 

✅ 일정 수정 (Update)

  • 일정 ID로 데이터를 조회한 후 비밀번호 검증
  • updated_date 값을 현재 시간으로 변경
  • 수정이 실패할 경우 예외 발생
더보기
@Override
public ScheduleAuthorDto updateSchedule(Long scheduleId, String title, String content, Long authorId, String password) {
    Timestamp updatedTime = new Timestamp(System.currentTimeMillis());

    Schedule schedule = scheduleRepository.findScheduleEntityById(scheduleId)
            .orElseThrow(() -> new ResponseStatusException(HttpStatus.NOT_FOUND, "존재하지 않는 일정입니다."));

    if (!schedule.getAuthorId().equals(authorId)) {
        throw new ResponseStatusException(HttpStatus.FORBIDDEN, "작성자만 일정을 수정할 수 있습니다.");
    }

    if (!schedule.getPassword().equals(password)) {
        throw new ResponseStatusException(HttpStatus.UNAUTHORIZED, "비밀번호가 일치하지 않습니다.");
    }

    int updatedRow = scheduleRepository.updatedSchedule(scheduleId, title, content, updatedTime, authorId);
    if (updatedRow == 0) {
        throw new ResponseStatusException(HttpStatus.NOT_FOUND, "일정 수정 실패");
    }

    return scheduleRepository.findScheduleByIdOrElseThrow(scheduleId);
}

✅ 일정 삭제 (Delete)

  • 비밀번호 검증 후 삭제 수행
  • 삭제 실패 시 예외 처리
더보기
@Override
public void deleteSchedule(Long scheduleId, String password) {
    Schedule schedule = scheduleRepository.findScheduleEntityById(scheduleId)
            .orElseThrow(() -> new ResponseStatusException(HttpStatus.NOT_FOUND, "존재하지 않는 일정입니다."));

    if (!schedule.getPassword().equals(password)) {
        throw new ResponseStatusException(HttpStatus.UNAUTHORIZED, "비밀번호가 일치하지 않습니다.");
    }

    int deleteRow = scheduleRepository.deleteSchedule(scheduleId);
    if (deleteRow == 0) {
        throw new ResponseStatusException(HttpStatus.NOT_FOUND, "일정 삭제 실패");
    }
}
 

 


5️⃣ API 엔드포인트 정리

HTTP Method HTTP Method URL기능
POST /schedules 일정 생성
GET /schedules 모든 일정 조회
GET /schedules/author/{authorId} 특정 작성자의 일정 조회
PUT /schedules/{scheduleId} 일정 수정 (비밀번호 필요)
DELETE /schedules/{scheduleId} 일정 삭제 (비밀번호 필요)

6️⃣ 배운 점

Spring Boot의 JDBC 활용법

  • JdbcTemplate을 사용한 SQL 쿼리 실행
  • SimpleJdbcInsert을 활용하여 Insert 간소화
  • RowMapper를 활용한 객체 매핑