
event Table 한개만으로 정보 처리.Create TABLE IF NOT EXISTS EVENT(
id int PRIMARY KEY AUTO_INCREMENT,
todo varchar(500) NOT NULL ,
password varchar(20) NOT NULL ,
createddate datetime NOT NULL ,
modifieddate datetime NOT NULL ,
startday date NOT NULL ,
endday date NOT NULL ,
creator varchar(5) NOT NULL
);
id : 일정들을 관리할 고유값 AUTO_INCREMENT 속성을 주어 자동으로 증가하게끔 생성.todo : 일정에 대한 내용 작성란password : 작성자외의 유저가 데이터삭제를 방지하기위한 보안책createddate : 일정 작성일modifieddate : 일정 수정일startday : 일정 시작일endday : 일정 종료일creator : 작성자 이름 @PostMapping("/schedul/")
public EventResponseDto createEvent(@RequestBody @Valid EventRequestDto requestDto) {
return eventService.createEvent(requestDto);
}
// 일정 생성 메소드.
public EventResponseDto createEvent(EventRequestDto requestDto) {
User userinfo = userRepository.findId(requestDto.getUser_id());
// 해당 유저가 존재하지 않는 경우
if (userinfo != null) {
// 해당 일자가 유효한지 검사 (Ex : 2024-02-30(false))
if (formatCheck(String.valueOf(requestDto.getStartday())) && formatCheck(String.valueOf(requestDto.getEndday()))) {
// 일정종료일이 시작일 보다 빠를경우 (Ex : 2024-09-30 ~ 2024-08-30)
if (requestDto.getStartday().before(requestDto.getEndday())) {
// RequestDto -> Entity
Event event = new Event(requestDto);
// DB 저장
Event saveEvent = eventRepository.save(event, userinfo);
// Entity -> ResponseDto
return new EventResponseDto(saveEvent);
} else {
throw new CustomException(END_DATE_BEFORE_START_DATE);
}
} else {
throw new CustomException(DATE_PARSE_ERROR);
}
} else {
throw new CustomException(USER_NOT_FOUND);
}
}
public Event save(Event event, User userinfo) {
KeyHolder keyHolder = new GeneratedKeyHolder();
// SQL 문 작성
String sql = "INSERT INTO event (todo, password,createddate,modifieddate,startday,endday,user_id,username) VALUES (?,?,?,?,?,?,?,?)";
java.sql.Timestamp timeNow = Timestamp.valueOf(LocalDateTime.now());
jdbcTemplate.update(con -> {
PreparedStatement preparedStatement = con.prepareStatement(sql,
Statement.RETURN_GENERATED_KEYS);
// Event 객체 필드에 있는 정보 주입.
preparedStatement.setString(1, event.getTodo());
preparedStatement.setString(2, event.getPassword());
preparedStatement.setString(3, String.valueOf(timeNow));
preparedStatement.setString(4, String.valueOf(timeNow));
preparedStatement.setString(5, event.getStartday().toString());
preparedStatement.setString(6, event.getEndday().toString());
preparedStatement.setString(7, event.getUser_id().toString());
preparedStatement.setString(8, userinfo.getUsername());
return preparedStatement;
},
keyHolder);
// DB Insert 후 받아온 기본키 및 생성/수정시간 삽입
Long id = keyHolder.getKey().longValue();
event.setId(id);
event.setCreateddate(timeNow);
event.setModifieddate(timeNow);
event.setUsername(userinfo.getUsername());
return event;
}
요구 조건 :
할일, 작성자명, 비밀번호, 작성/수정일을 저장작성/수정일은 날짜와 시간을 모두 포함한 형태ID 를 자동생성 관리작성/수정일은 동일함.3 Layer 구조 설계로 각계층의 책임을 가지고 설계.
요청을 DTO에 담아서 Entity 화 후 Repository에게 전달하면 Repository는 해당 객체를 가지고 쿼리문을 작성, 전송하고 전송한 객체를 반환한다.
생성/수정일은 초기값은 동일하기에 메소드 진입시 현재시간을 저장.
일정의 시작/종료일을 추가하였으며, 종료일이 시작일보다 빠를경우 에러발생.
입력 값 중 NULL 이 존재할경우 에러발생.
성공적 입력

DB 입력 형태

NULL 값이 존재할 경우 예외

일정 종료일이 시작일보다 빠를경우 예외

서버 에러로그

// 일정을 조회하는 컨트롤러 메소드 정보 기입여부에 따른 출력이 달라짐.
@GetMapping("/schedul/")
public List<EventResponseDto> getAllEvents(@RequestParam(required = false) String creator, @RequestParam(required = false) String modifieddate) {
return eventService.getEventAll(creator, modifieddate);
}
// 일정 조회 메소드. 파라미터 여부에 따른 결과가 달라짐.
public List<EventResponseDto> getEventAll(String creator, String modifieddate) {
return eventRepository.findAll(creator, modifieddate);
}
// Client 에서 보내온 파라미터값에 따른 DB 조회 메소드
// 조건에 따른 WHERE 절 변화
public List<EventResponseDto> findAll(String creator, String modifieddate) {
// 둘다 기입하지 않았을시 모든 일정 조회
String sql = "SELECT * FROM event";
// 작성자, 수정일을 둘다 기입했을시.
if (creator != null && modifieddate != null) {
sql += " WHERE creator = " + "'" + creator + "'" + " OR date_format(modifieddate,'%Y-%m-%d') = " + "'" + modifieddate + "'";
}
// 수정일만 기입시 수정일 기준으로 DB 조회.
else if (modifieddate != null) {
sql += " WHERE date_format(modifieddate,'%Y-%m-%d') = " + "'" + modifieddate + "'";
}
// 작성자만 기입시 작성자 기준으로 DB 조회.
else if (creator != null) {
sql += " WHERE creator = " + "'" + creator + "'";
}
sql += " ORDER BY modifieddate DESC";
return jdbcTemplate.query(sql, new RowMapper<EventResponseDto>() {
@Override
public EventResponseDto mapRow(ResultSet rs, int rowNum) throws SQLException {
// SQL 의 결과로 받아온 Memo 데이터들을 EventResponseDto 타입으로 변환해줄 메서드
Long id = rs.getLong("id");
String todo = rs.getString("todo");
java.sql.Timestamp createddate = rs.getTimestamp("createddate");
java.sql.Timestamp modifieddate = rs.getTimestamp("modifieddate");
java.sql.Date startday = rs.getDate("startday");
java.sql.Date endday = rs.getDate("endday");
String creator = rs.getString("creator");
return new EventResponseDto(id, todo, createddate, modifieddate, startday, endday, creator);
}
});
}
요구 조건 :
수정일 (YYYY-MM-DD), 작성자명 을 이용한 일정목록 조회수정일 을 기준으로 내림차순 조회.@RequestParam(required = false) 기능을 이용하여 필수요소로 사용을 억제.
들어온 값 (작성일 OR 수정일) 에 따라 SQL문의 WHERE 절을 나눠서 사용.
모두조회 (값 없음)

모두조회 (작성자명 개발자1 혹은 2024-09-30에 수정데이터)

// ID 값을 기준으로 정보를 조회하는 컨트롤러 메소드.
@GetMapping("/schedul/id/")
public EventResponseDto getEventById(@RequestParam Long id) {
return eventService.getEventById(id);
}
// 해당 일정 ID의 정보 조회 메소드.
public EventResponseDto getEventById(Long id) {
Event event = eventRepository.findId(id);
if (event != null) {
return new EventResponseDto(event);
} else {
throw new CustomException(OUT_OF_RANGE);
}
}
// ID 값을 기준으로 DB 조회 메소드
public Event findId(Long id) {
String sql = "SELECT * FROM event WHERE id=?";
return jdbcTemplate.query(sql, resultSet -> {
if (resultSet.next()) {
Event event = new Event();
event.setId(resultSet.getLong("id"));
event.setTodo(resultSet.getString("todo"));
event.setPassword(resultSet.getString("password"));
event.setCreateddate(resultSet.getTimestamp("createddate"));
event.setModifieddate(resultSet.getTimestamp("modifieddate"));
event.setStartday(resultSet.getDate("startday"));
event.setEndday(resultSet.getDate("endday"));
event.setCreator(resultSet.getString("creator"));
return event;
} else {
return null;
}
}, id);
}
요구 조건 :
고유 식별자(ID)를 사용하여 조회합니다.ID 의 값은 필수이기에 ID값을 받고 DB내의 해당 ID를 가지는 일정ROW가 존재하는지 검사.
존재하지 않는다면 throw new CustomException(OUT_OF_RANGE); 을 통해 커스텀 예외를 발생시킨다.
조회성공

조회실패

// ID 값을 기준으로 정보를 수정하는 컨트롤러 메소드
// DTO 를 Body 로 받는 이유는 URL 이 아닌 Body 로 받으면서 보안성 향상.
@PutMapping("/schedul/")
public Long updateEvent(@RequestParam Long id, @RequestBody @Valid EventRequestDto requestDto) {
return eventService.updateEvent(id, requestDto);
}
public Long updateEvent(Long id, EventRequestDto requestDto) {
Event event = eventRepository.findId(id);
// 글을 작성한 유저가 접근했는지 검사하기위한 UserInfo 객체 생성
User userinfo = userRepository.findId(requestDto.getUser_id());
// 해당 ID 일정이 존재하는 경우
if (event != null || userinfo != null) {
// 접근 User 가 일정을 작성한 유저 이면서 게시글에 대한 비밀번호가 일치하는지?
if (requestDto.getPassword().equals(event.getPassword()) && userinfo.getId().equals(event.getUser_id())) {
// 해당 일자가 유효한지 검사 (Ex : 2024-02-30(false))
if (formatCheck(String.valueOf(requestDto.getStartday())) && formatCheck(String.valueOf(requestDto.getEndday()))) {
// 일정종료일이 시작일 보다 빠를경우 (Ex : 2024-09-30 ~ 2024-08-30)
if (requestDto.getStartday().before(requestDto.getEndday())) {
eventRepository.update(id, requestDto);
return id;
} else {
throw new CustomException(END_DATE_BEFORE_START_DATE);
}
} else {
throw new CustomException(DATE_PARSE_ERROR);
}
} else {
throw new CustomException(USER_INFO_MISMATCH);
}
} else {
throw new CustomException(OUT_OF_RANGE);
}
}
// 일정 ID 값을 기준으로 DB 데이터 수정 메소드
public void update(Long id, EventRequestDto requestDto) {
java.sql.Timestamp modifieddate = Timestamp.valueOf(LocalDateTime.now());
String sql = "UPDATE event SET todo=?, modifieddate=?, startday =?, endday=? WHERE id =?";
jdbcTemplate.update(sql, requestDto.getTodo(), modifieddate, requestDto.getStartday(), requestDto.getEndday(), id);
}
고유 식별자(ID)를 사용하여 정보를 수정합니다.할일 , 작성자명 만 수정 가능비밀번호를 함께 전달작성일은 변경이 불가, 수정일은 수정 완료시 시점으로 변경.정상수정


비밀번호 오류

// ID 값을 기준으로 정보를 삭제하는 컨트롤러 메소드
// DTO 를 Body 로 받는 이유는 URL 이 아닌 Body 로 받으면서 보안성 향상.
@DeleteMapping("/schedul/")
public Long deleteEvent(@RequestParam Long id, @RequestBody EventRequestDto requestDto) {
return eventService.deleteEvent(id, requestDto);
}
// ID 기준의 DB 삭제 메소드
public Long deleteEvent(Long id, EventRequestDto requestDto) {
Event event = eventRepository.findId(id);
// 해당 ID가 존재하면서 게시글 비밀번호와 동일한 시 정보 삭제
if (event != null) if (requestDto.getPassword().equals(event.getPassword())) {
eventRepository.delete(id);
return id;
} else {
throw new CustomException(PASSWORD_EXCEPTION);
}
else {
throw new CustomException(OUT_OF_RANGE);
}
}
// ID 값을 기준으로 DB 데이터 삭제 메소드
public void delete(Long id) {
String sql = "DELETE FROM event WHERE id=?";
jdbcTemplate.update(sql, id);
}
요구 조건 :
고유 식별자(ID)를 사용하여 정보를 삭제합니다.비밀번호를 함께 전달서비스 클래스에서 현재 선택한 ID의 Row 객체와 requestDto 의 비밀번호를 비교, 동일할시 삭제, 불가능할시 예외발생.
예외 발생구문은 수정작업과 동일.
정상삭제


create table if not exists creator (
id int PRIMARY KEY AUTO_INCREMENT,
username varchar(5) not null ,
email varchar(30) not null ,
join_date DATETIME not null ,
update_date datetime not null
);
id : User 들의 중복 데이터를 구분하기 위한 고유값 AUTO_INCREMENT 속성으로 자동 상승한다.username : 기존의 event Table 이 가지던 유저 이름.email : 유저가 가지는 e-mail 값 정규식으로 인해 이메일 패턴값만 받는다.join_date : 유저가 등록된 일자.update_date : 유저 정보 가 수정된 일자.ALTER TABLE `calendar`.`event`
CHANGE COLUMN `creator` `user_id` INT NOT NULL ;
ALTER TABLE event ADD username VARCHAR(5) NOT NULL;
ALTER TABLE event
ADD FOREIGN KEY (user_id) REFERENCES user(id) ON DELETE CASCADE
외래키 로 받아온다.ON DELETE CASCADE 를 통해 삭제된 User의 ID와 동일할 경우 일정을 전부 삭제한다. // 단일 User Data 를 DB 상에서 수정하는 메소드
public void update(Long id, String username) {
// 수정시간 입력을 위한 Timestamp 생성
Timestamp update_date = Timestamp.valueOf(LocalDateTime.now());
// 해당 ID를 가진 UserData 수정
String sql = "UPDATE user SET username = ?, update_date = ? WHERE id = ?";
jdbcTemplate.update(sql, username, update_date, id);
// event Table 에 일정을 가지고 있을 경우 변경값으로 함께 변경
sql = "UPDATE event SET username = ? WHERE user_id = ?";
jdbcTemplate.update(sql, username, id);
}

생성

변경(username)

삭제

참조 :
https://daily-life-of-bsh.tistory.com/207
https://velog.io/@eensungkim/ON-DELETE-CASCADE-feat.-row-%ED%95%9C-%EB%B2%88%EC%97%90-%EC%A7%80%EC%9A%B0%EB%8A%94-%EB%B0%A9%EB%B2%95-TIL-78%EC%9D%BC%EC%B0%A8
@GetMapping("/schedul/page/")
public ResponseEntity getAllEventsPage(@Positive @RequestParam int page, @Positive @RequestParam int size) {
Page<Event> eventsPage = eventService.findPageEvents(page - 1, size);
PageInfo pageInfo = new PageInfo(page, size, (int) eventsPage.getTotalElements(), eventsPage.getTotalPages());
List<Event> events = eventsPage.getContent();
List<EventResponseDto> respones = eventMapper.eventsToEventResponseDtos(events);
return new ResponseEntity<>(new PageResponseDto(respones, pageInfo), HttpStatus.OK);
}
public Page<Event> findPageEvents(int page, int size) {
PageRequest pageRequest = PageRequest.of(page, size);
return eventPageRepository.findAllByOrderByIdDesc(pageRequest);
}
public interface EventPageRepository extends CrudRepository<Event, Long> {
Page<Event> findAllByOrderByIdDesc(Pageable pageable);
}
@Mapper(componentModel = "spring")
public interface EventMapper {
// Mapper 를 DI 받아 Entity 와 Dto 를 맵핑 해주는 역할
List<EventResponseDto> eventsToEventResponseDtos(List<Event> events);
}
@Getter
@Setter
@AllArgsConstructor
// 전체 일정을 담고있는 Data 와 page Info 를 가지는 DTO
public class PageResponseDto {
private List<EventResponseDto> data;
private PageInfo pageInfo;
}
@Getter
@Setter
@AllArgsConstructor
// Page 의 정보를 가지고 있는 Info 객체
public class PageInfo {
private int page;
private int size;
private int totalElements;
private int totalPages;
}
페이지번호,크기를 기준으로 모두 조회.작성자 이름 포함빈 배열 포함Offset 구현방식으로 구현.PageRequest 클래스는 Pageable 인터페이스를 구현한 추상화 객체를 상속받은 클래스로 이를 통해 객체를 반환 받는다.CrudRepository를 상속받아 Repository를 구현 했으므로 메소드 명 을 적절히 작성하여 필요로 하는 데이터를 DB로 요청이 필요.Event의 id 값 을 기준으로 정보를 가져온다는 뜻.PageResponseDto 객체를 통하여 JSON 타입으로 반환해준다.
page : 1, Size : 5

page : 3, Size : 5 (빈배열 반환)

참조 :
https://velog.io/@sussa3007/Spring-Spring-DATA-JDBC-Pagination-API#-repository-%EA%B5%AC%ED%98%84
https://mason-lee.tistory.com/108
https://clgitory.tistory.com/m/40









📦
├─ .gitignore
├─ build.gradle
├─ gradle
│ └─ wrapper
│ ├─ gradle-wrapper.jar
│ └─ gradle-wrapper.properties
├─ gradlew
├─ gradlew.bat
├─ settings.gradle
└─ src
├─ main
│ ├─ java
│ │ └─ com
│ │ └─ sparta
│ │ └─ calendarprojects
│ │ ├─ CalendarProjectsApplication.java
│ │ ├─ controller : 유저, 일정 관련 API 컨트롤러 패키지
│ │ │ ├─ EventController.java : 일정 관리 컨트롤러
│ │ │ └─ UserController.java : 유저 관리 컨트롤러
│ │ ├─ dto : 유저, 일정 관련 정보처리 DTO 패키지
│ │ │ ├─ EventRequestDto.java : 일정관련 요청정보 DTO
│ │ │ ├─ EventResponseDto.java : 일정관련 응답정보 DTO
│ │ │ ├─ PageResponseDto.java : 일정 페이징 정보 DTO
│ │ │ ├─ UserReponseDto.java : 유저관련 응답정보 DTO
│ │ │ └─ UserRequestDto.java : 유저관련 요청정보 DTO
│ │ ├─ entity : DB 조회용 Entity 패키지
│ │ │ ├─ Event.java : 일정 관련 Entity
│ │ │ └─ User.java : 유저 관련 Entity
│ │ ├─ exception : 예외처리 패키지
│ │ │ ├─ CustomErrorCode.java : 예외발생 에러 코드 ENUM 클래스
│ │ │ ├─ CustomException.java : 예외 객체 생성용 상속 클래스
│ │ │ ├─ CustomExceptionHandler.java : 예외 핸들링 클래스
│ │ │ └─ ErrorResponse.java : 예외정보 DTO
│ │ ├─ info : 서버안에서 운용하는 Info 객체
│ │ │ └─ PageInfo.java : 페이징 정보 객체
│ │ ├─ mapper : 페이징 기능 활용 mapper 패키지
│ │ │ └─ EventMapper.java : Page 기능을 활용해 DB에 객체들을 맵핑해주는 Mapper
│ │ ├─ repository : 유저, 일정 관련 DB 조회 레파지토리 패키지
│ │ │ ├─ EventPageRepository.java : 페이징 기능 활용을 위한 인터페이스 구현체 레파지토리
│ │ │ ├─ EventRepository.java : 일정 관련 DB 관리 레파지토리
│ │ │ └─ UserRepository.java : 유저 관련 DB 관리 레파지토리
│ │ └─ service : 유저, 일정 관련 서비스 로직 패키지
│ │ ├─ EventService.java : 일정 관련 서비스로직
│ │ └─ UserService.java : 유저 관련 서비스로직
│ └─ resources
│ └─ application.properties
└─ test
└─ java
└─ com
└─ sparta
└─ calendarprojects
└─ CalendarProjectsApplicationTests.java
©generated by Project Tree Generator