오늘 공부한 것
* Spring LV1 과제 하기
LV1 과제를 하기위해 강의를 듣고서
과제 시작을 하게 되었다
다행히 겨우겨우 완성 할 수 있었다LV2 부터는 다른 분과 함께 팀을 이루어서 하게되는데폐끼치는 일이 없길 바란다. ㅠㅠ
1. Use Case
2. APi 명세
기능 | Method | Url | Request | Response |
전체 게시글 목록 조회 | get | /api/post | - | id, title, username, contents, createAt, modifiedAt |
게시글 작성 | post | /api/post | - | id, title, username, contents, createAt, modifiedAt |
선택한 게시글 조회 | get | /api/post/{id} | title, username, contents, password |
id, title, username, contents, createAt, modifiedAt |
선택한 게시글 수정 | put | /api/post/{id} | title, username, contents, password |
id, title, username, contents, createAt, modifiedAt |
선택한 게시글 삭제 | delete | /api/post/{id} | password | success : true |
3. 작성 코드
1) BoardController
package com.sparta.board.controller;
import com.sparta.board.dto.BoardRequestDto;
import com.sparta.board.dto.BoardResponseDto;
import com.sparta.board.service.BoardSurvice;
import org.springframework.web.bind.annotation.*;
import java.util.List;
// Spring 3 Layer Annotation 중 하나 (Controller, Service, Reository)
// 각 계층을 Bean으로 등록할 때 사용
// 해당 어노테이션 안에는 이미 @Component가 추가됨
// @Component 개발자가 직접 작성한 Class를 Bean으로 등록하기위한 어노테이션
@RestController
// 아래에서 구현된 각 API의 URL에 공통적으로 들어가는 부분
// 동일한 URL 부분의 반복을 줄여 줄 수 있다
@RequestMapping("/api")
public class BoardController {
// BoardSurvice 와 연결 인스턴스화
private final BoardSurvice boardSurvice;
// 생성자
public BoardController(BoardSurvice boardSurvice) {
this.boardSurvice = boardSurvice;
}
@PostMapping("/post")
// BoardResponseDto 반환타입, createBoard 메소드명(원하는대로)
// @RequestBody : Post 안에 저장된 body 값들을 key:value 형태 (JSON 타입)으로 짝지음 body에 들어오는 데이터들을 가지고옴
// BoardRequestDto : JSON 타입으로 넘어오는 데이터를 받는 객체(데이터를 저장할 공간)
// requestDto : requestDto 매개변수에 데이터를 담아서, boardService의 createBoard 메서드로 실어보냄
public BoardResponseDto createBoard(@RequestBody BoardRequestDto requestDto){
// 생성
// 매개변수 requestDto 를 메소드 createBoard를 사용하여 boardSurvice로 반환(boardSurvice와 연결)
return boardSurvice.createBoard(requestDto);
}
@GetMapping("/post")
// List 형태로
// BoardResponseDto 반환타입 getBoardList 메소드명 () 전부 Client에게 반환하므로 비워둠
public List<BoardResponseDto> getBoardList(){
// DB 조회
// getBoardList 메소드를 사용하여 boardSurvice 와 연결
return boardSurvice.getBoardList();
}
@GetMapping("/post/{id}")
// List 형태로
// BoardResponseDto 반환타입 getBoard 메소드명 (@PathVariable 게시글마다 생성되는 id 값을 넣기위해 사용 Long 타입 id)
public List<BoardResponseDto> getBoard(@PathVariable Long id) {
// 선택한 DB 내용 조회
// getBoard 메소드를 사용하여 boardSurvice 와 연결
return boardSurvice.getBoard(id);
}
@PutMapping("/post/{id}")
// 수정을 위해 BoardResponseDto의 필드값이 필요
public BoardResponseDto updateBoard(@PathVariable Long id, @RequestBody BoardRequestDto requestDto) {
// DB 내용 수정
// requestDto의 id를 가지고옴
return boardSurvice.updateBoard(id, requestDto);
}
@DeleteMapping("/post/{id}")
public BoardResponseDto deleteBoard(@PathVariable Long id, @RequestBody BoardRequestDto requestDto){
// DB 내용 삭제
return boardSurvice.deleteBoard(id, requestDto);
}
}
2) BoardRequestDto
package com.sparta.board.dto;
import lombok.Getter;
import java.time.LocalDateTime;
@Getter
public class BoardRequestDto {
//사용자가 요청한 데이터(입력한 값)
private long id;
private String title;
private String username;
private String contents;
private String password;
private LocalDateTime createAt;
private LocalDateTime modifiedAt;
}
3) BoardResponseDto
package com.sparta.board.dto;
import com.sparta.board.entity.Board;
import lombok.Getter;
import java.time.LocalDateTime;
@Getter
public class BoardResponseDto {
private long id; // 게시글 구분을 위한 id
private String title; // 제목
private String username; // 작성자명
private String contents; // 작성내용
private String password; //비밀번호
private LocalDateTime createAt; // 게시글 생성 날짜
private LocalDateTime modifiedAt; // 게시글 수정 날짜
private String msg; // 게시글 삭제시, 삭제 성공 메시지
// Entity -> ResponseDto 변환
// Board 라는 Entitiy에 저장값들을 호출해서 getxxx() 메서드를 이용해 BoardResponseDto의 필드에 담음
public BoardResponseDto(Board board) {
this.id = board.getId();
this.username = board.getUsername();
this.title = board.getTitle();
this.password = board.getPassword();
this.contents = board.getContents();
this.createAt = board.getCreatedAt();
this.modifiedAt = board.getModifiedAt();
}
// 게시글 삭제시, 삭제 성공 메시지
public BoardResponseDto(String msg){
this.msg = msg;
}
}
4) Board(Entity)
package com.sparta.board.entity;
import com.sparta.board.dto.BoardRequestDto;
import jakarta.persistence.*;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
// JPA가 관리 할 수 있는 Entity 클래스로 지정
@Entity
@Getter
@Setter
// 매핑할 테이블 이름을 지정
@Table(name = "board")
// 어노테이션을 이용한 생성자 주입
@NoArgsConstructor
public class Board extends Timestamped {
// 필드, 기본키 지정
@Id
// 기본 키값을 자동으로 생성하는 전략을 지정할때 사용
// 데이터베이스의 자동증가 기능을 통해 id 값이 자동으로 생성되고 할당됨
@GeneratedValue(strategy = GenerationType.IDENTITY)
private long id;
// 필드를 데이터베이스 컬럼과 매핑할때 사용
// name : 해당 필드를 어떤 컬럼과 매핑할것인지 지정 title 필드가 데이터베이스 테이블의 title 컬럼과 매핑됨
// nullable 컬럼이 null 값을 허용하는지 여부 설정 false인 경우 해덩 컬럼에 값이 반듯이 있어야함
@Column(name = "title", nullable = false)
private String title;
@Column(name = "username", nullable = false)
private String username;
@Column(name = "contents", nullable = false, length = 1000)
private String contents;
@Column(name = "password", nullable = false)
private String password;
// 게시글 작성
// Entity 클래스와 Controller 간의 데이터 전달을 위해 사용함
// 클라이언트에서 들어오는 요청 데이터를 담고 Entity로 변환되거나 Entity에서 추출된 정보를 클라이언트에게 보낼 때 사용
// Board Entity 클래스의 생성자를 정의함 객체가 생성괼때 초기화 작업 수행
public Board (BoardRequestDto requestDto){
// Board Entity 객체의 username 필드를 BoardRequestDto 객체의 getUsername 메서드로 부터 얻은 값으로 초기화
this.username = requestDto.getUsername();
this.contents = requestDto.getContents();
this.password = requestDto.getPassword();
this.title = requestDto.getTitle();
}
// 게시글 수정
// update 메서드가 Board Entity 객체를 주어진 BoardRequestDto 객체의 값으로 업데이트 하는 역활을 수행
public void update(BoardRequestDto requestDto){
this.username = requestDto.getUsername();
this.contents = requestDto.getContents();
this.password = requestDto.getPassword();
this.title = requestDto.getTitle();
}
}
5) Timestamped(Entity)
package com.sparta.board.entity;
import jakarta.persistence.*;
import lombok.Getter;
import org.springframework.data.annotation.CreatedDate;
import org.springframework.data.annotation.LastModifiedDate;
import org.springframework.data.jpa.domain.support.AuditingEntityListener;
import java.time.LocalDateTime;
@Getter
// JPA Entity 클래스들이 해당 추상 클래스를 상속할 경우 createdAt, modifiedAt 처럼
// 추상 클래스에 선언한 멤버변수를 컬럼으로 인식할 수 있습니다.
@MappedSuperclass
//해당 클래스에 Auditing 기능을 포함시켜 줍니다.
@EntityListeners(AuditingEntityListener.class)
public abstract class Timestamped {
// Entity 객체가 생성되어 저장될 때 시간이 자동으로 저장됩니다.
@CreatedDate
// 업데이트가 되지않게 막아줌
// 최초 생성시간만 저장됨
@Column(updatable = false)
//날짜 타입(java.util.Date, java.util.Calendar)을 매핑할 때 사용합니다.
@Temporal(TemporalType.TIMESTAMP)
private LocalDateTime createdAt;
@LastModifiedDate //조회한 Entity 객체의 값을 변경할 때 변경된 시간이 자동으로 저장됩니다.
@Column
@Temporal(TemporalType.TIMESTAMP)
// *DATE : ex) 2023-01-01
// *TIME : ex) 20:21:14
// *TIMESTAMP : ex) 2023-01-01 20:22:38.771000
private LocalDateTime modifiedAt;
}
6) BoardRepositoty
package com.sparta.board.repository;
import com.sparta.board.entity.Board;
import org.springframework.data.jpa.repository.JpaRepository;
import java.util.List;
//JpaRepository<Board, Long>
// @Entity 클래스 id 데이터 타입
public interface BoardRepository extends JpaRepository<Board, Long> {
// findAllBy OrderBy ModifiedAt Desc()
// 전체셀렉트 정렬 이 필드로 내림차순.
// Memo 테이블에서 ModifiedAt 즉, 수정 시간을 기준으로 전체 데이터를
// 내림차순으로 가져오는 SQL을 실행하는 메서드
List<Board> findAllByOrderByModifiedAtDesc();
}
7) BoardSurvice
package com.sparta.board.service;
import com.sparta.board.dto.BoardRequestDto;
import com.sparta.board.dto.BoardResponseDto;
import com.sparta.board.entity.Board;
import com.sparta.board.repository.BoardRepository;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import java.util.List;
// Spring 3 Layer Annotation 중 하나 (Controller, Service, Reository)
@Service
// 어노테이션을 이용한 생성자 주입
@RequiredArgsConstructor
public class BoardSurvice {
// BoardRepository 와 연결 인스턴스화
private final BoardRepository boardRepository;
public BoardResponseDto createBoard(BoardRequestDto requestDto) {
//생성
// RequestDto -> Entity
// requestDto 열쇠를 담아서 Board 객체로 변환
Board board = new Board(requestDto);
// DB 저장
// 변환된 결과를 담은 board를 담아 boardRepository의
// save 메서드를 호출해서 DB에 저장하고 그 결과값을 saveBoard 변수에 담는다
Board saveBoard = boardRepository.save(board);
// Entity -> ResponseDto
// saveBoard 변수에 담긴 결과값을 담아서 boardResponseDto 객체로 변환
BoardResponseDto boardResponseDto = new BoardResponseDto(board);
// boardResponseDto를 반환
return boardResponseDto;
}
public List<BoardResponseDto> getBoardList() {
// DB조회
// findAllBy OrderBy ModifiedAt Desc()
// 전체셀렉트 정렬 이 필드로 내림차순
// stream().map(BoardResponseDto::new).toList(); BoardResponseDto 객체를 List 형태로 호출
return boardRepository.findAllByOrderByModifiedAtDesc().stream().map(BoardResponseDto::new).toList();
}
public List<BoardResponseDto> getBoard(Long id) {
// DB id로 조회
// boardRepository의 findById 메서드로 매개변수로 넣은 해당 id 값을 찾는다
return boardRepository.findById(id).stream().map(BoardResponseDto::new).toList();
}
public BoardResponseDto updateBoard(Long id, BoardRequestDto requestDto) {
// DB에 존재 하는지 확인
Board board = findBoard(id);
// DB 내용 수정
// 비밀 번호 일치여부 확인
// board Entity에 저장된 password와 사용자가 requestDto로 입력한 password를
// equals()메서드를 이용해서 일치 여부 학인
if (board.getPassword().equals(requestDto.getPassword())) {
//일치할 경우 board에 접근하여 requestDto 대로 update 한다
board.update(requestDto);
} else {
return new BoardResponseDto("비밀번호가 일치하지 않습니다");
}
return new BoardResponseDto(board);
}
public BoardResponseDto deleteBoard(Long id, BoardRequestDto requestDto) {
// DB에 존재 하는지 확인
Board board = findBoard(id);
// DB 내용 삭제
// boardRepository.delete(board);
// return id;
if (board.getPassword().equals(requestDto.getPassword())) {
//일치할 경우 boardRepository 접근하여 board 를 delete 한다
boardRepository.delete(board);
} else {
return new BoardResponseDto("비밀번호가 일치하지 않습니다");
}
return new BoardResponseDto("게시글을 삭제했습니다");
}
// id 일치여부 확인
// 수정과 삭제 공통 부분
private Board findBoard(Long id){
return boardRepository.findById(id).orElseThrow(()->
new IllegalArgumentException("선택한 게시글은 존재하지 않습니다")
);
}
}
8) BoardApplication
package com.sparta.board;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.data.jpa.repository.config.EnableJpaAuditing;
// Timestamped 클래스에서 Auditing 기능을 사용하기 위함
@EnableJpaAuditing
@SpringBootApplication
public class BoardApplication {
public static void main(String[] args) {
SpringApplication.run(BoardApplication.class, args);
}
}
9) application.properties
spring.datasource.url=jdbc:mysql://localhost:3306/board
spring.datasource.username=root
spring.datasource.password= 비밀번호
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
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
10) Gradle
plugins {
id 'java'
id 'org.springframework.boot' version '3.1.3'
id 'io.spring.dependency-management' version '1.1.3'
}
group = 'com.sparta'
version = '0.0.1-SNAPSHOT'
java {
sourceCompatibility = '17'
}
configurations {
compileOnly {
extendsFrom annotationProcessor
}
}
repositories {
mavenCentral()
}
dependencies {
// JPA 설정
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
// MySQL
implementation 'mysql:mysql-connector-java:8.0.28'
implementation 'org.springframework.boot:spring-boot-starter-thymeleaf'
implementation 'org.springframework.boot:spring-boot-starter-web'
compileOnly 'org.projectlombok:lombok'
annotationProcessor 'org.projectlombok:lombok'
testImplementation 'org.springframework.boot:spring-boot-starter-test'
}
tasks.named('test') {
useJUnitPlatform()
}
작성을 하면서도 연계되는 부분이 헷갈려서 관계도를 해보았다
그래도 복잡복잡하긴한데 하다보면 익숙해 지려나?
4. 관계도
'항해99' 카테고리의 다른 글
23.08.31 항해 99 16기 주특기 Spring 6일차 (0) | 2023.08.31 |
---|---|
23.08.30 항해 99 16기 주특기 Spring 5일차 (0) | 2023.08.30 |
23.08.28 항해 99 16기 주특기 Spring 3일차 (0) | 2023.08.28 |
23.08.21~08.27 항해 99 16기 2주차 회고록 (0) | 2023.08.27 |
23.08.26 항해 99 16기 주특기 Spring 2일차 (0) | 2023.08.26 |