vue + jpa project (7) - 게시판 테이블 생성 및 백엔드 구성 본문

프로그램/Vue.js

vue + jpa project (7) - 게시판 테이블 생성 및 백엔드 구성

반응형

이제 Vue 게시판 목록을 실제 DB와 연동하여 조회를 하기위한 작업을 진행한다.

우선 게시판의 테이블 정보를 아래와 같이 구성하려고 한다. 일반적인 구성이다. 

 

1. 게시판 Entity

     실제 패키지 명은 업무단위로 분리해서 생성하는게 좋으나 일단 여기서는 같이 사용하겠다.

     패키지는 entity를 추가로 붙혀서 생성한다. 

@Data
@AllArgsConstructor
@NoArgsConstructor
@Builder
@Table(name="board")
@Entity
public class BoardEntity {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long boardNo;
    
    @Column(name = "title", nullable = false, columnDefinition = "VARCHAR(200) CHARACTER SET UTF8")
    private String title;
    
    @Column(length = 50, nullable = false)
    private String writer;
    
    @Lob
    private String content;
    
    @Column(length = 200, nullable = true)
    private String pictureUrl;

    @Column(nullable = true, columnDefinition = "datetime(6)")
    @JsonFormat(pattern = "yyyy-MM-dd")
    @CreationTimestamp
    private LocalDateTime regDate;
    
    
    @Column(nullable = true, columnDefinition = "datetime(6)")
    @JsonFormat(pattern = "yyyy-MM-dd")
    //@UpdateTimestamp
    private LocalDateTime updDate;
    
}

 

STS에서 서버를 기동 시키면 자동으로 board 테이블이 생성이 된다.

자동으로 생성되는 이유는 application.yml 에서 jpa: hibernate: ddl-auto: 에서 update  설정해서이다.

update는 변경이 된 것이 있으면 그것만 변경되도록 하는 ddl 옵션 이다. 

기존에 board 테이블이 없다면 생성이 될 것이고, 변경사항이 없다면 skip이 될것이다.

 

생성된 테이블을 사용하기 전에 유의할 점은 마리아DB의 기본 CharSet 이 라틴으로 되어있어서 테이블의 Charset이

라틴으로 생성이 될 것이다. 물론 기본 Charset을 바꾸고 하면 좋겠지만 일단 이 부분은 따로 공부를 하는 방향으로 하고 여기선 Skip하겠다. (구글링하면 잘 나오는 듯 하다.)

아래의 이미지처럼 Charset과 Collations를 수정하고, 각각의 필드 (문자타입만)도 동일하게 바꾸자.

그리고 테스트를 위해서 임시로 아래의 쿼리를 이용하여 데이터를 생성한다.

INSERT INTO test.board (title,writer,content,picture_url,reg_date) VALUES
	 ('게시글 제목1','작성자1','게시글 내용1',NULL,'2023-08-17 18:00:20.727'),
	 ('게시글 제목2','작성자2','게시글 내용2',NULL,'2023-08-17 18:00:20.734'),
	 ('게시글 제목3','작성자3','게시글 내용3',NULL,'2023-08-17 18:00:20.734'),
	 ('게시글 제목4','작성자4','게시글 내용4',NULL,'2023-08-17 18:00:20.737'),
	 ('게시글 제목5','작성자5','게시글 내용5',NULL,'2023-08-17 18:00:20.738'),
	 ('게시글 제목6','작성자6','게시글 내용6',NULL,'2023-08-17 18:00:20.739'),
	 ('게시글 제목7','작성자7','게시글 내용7',NULL,'2023-08-17 18:00:20.740'),
	 ('게시글 제목8','작성자8','게시글 내용8',NULL,'2023-08-17 18:00:20.741'),
	 ('게시글 제목9','작성자9','게시글 내용9',NULL,'2023-08-17 18:00:20.741'),
	 ('게시글 제목10','작성자10','게시글 내용10',NULL,'2023-08-17 18:00:20.742');
INSERT INTO test.board (title,writer,content,picture_url,reg_date) VALUES
	 ('게시글 제목11','작성자11','게시글 내용11',NULL,'2023-08-17 18:00:20.743'),
	 ('게시글 제목12','작성자12','게시글 내용12',NULL,'2023-08-17 18:00:20.744'),
	 ('게시글 제목13','작성자13','게시글 내용13',NULL,'2023-08-17 18:00:20.745'),
	 ('게시글 제목14','작성자14','게시글 내용14',NULL,'2023-08-17 18:00:20.746'),
	 ('게시글 제목15','작성자15','게시글 내용15',NULL,'2023-08-17 18:00:20.747'),
	 ('게시글 제목16','작성자16','게시글 내용16',NULL,'2023-08-17 18:00:20.748'),
	 ('게시글 제목17','작성자17','게시글 내용17',NULL,'2023-08-17 18:00:20.749'),
	 ('게시글 제목18','작성자18','게시글 내용18',NULL,'2023-08-17 18:00:20.750'),
	 ('게시글 제목19','작성자19','게시글 내용19',NULL,'2023-08-17 18:00:20.751'),
	 ('게시글 제목20','작성자20','게시글 내용20',NULL,'2023-08-17 18:00:20.752');

넘어가기 전에 초반에 백엔드 구성했을때 messages.properties를 이용한 부분이 있었다. 

이 부분을 실제 개발에 이용해 보도록 하자. 

messages.properties 파일을 열어서 error.notFound = {0}를 찾을 수 없습니다. 라는 문구를 추가해보자.

이 부분은 아래쪽에 서비스에서 오류가 발생했을 때 문구를 가져와서 리턴해주도록 할 것이다.


 

2. 게시판 Dto

    패키지는 dto를 추가로 붙혀서 생성한다. 

@Data
@AllArgsConstructor
@NoArgsConstructor
@Builder
public class BoardDto implements Serializable {
    private static final long serialVersionUID = 3056502198773946984L;

    private Long boardNo;
    private String title;
    private String writer;
    private String content;
    private String pictureUrl;
    private String regDate;
    private String updDate;
}

 

3. 게시판 컨트롤러

     패키지는 controller를 추가로 붙혀서 생성한다. 

@RequiredArgsConstructor
@CrossOrigin
@RestController
public class BoardController {

    private final BoardService boardService;

    @GetMapping("/board/list")
    public List<BoardDto> boardList() { return boardService.getBoardList(); }

    @GetMapping("/board/{no}")
    public BoardDto getBoard(@PathVariable(name = "no") Long no) {
        return boardService.getBoard(no);
    }

    @PostMapping("/board")
    public BoardEntity create(@RequestBody BoardDto boardDto) {
        return boardService.create(boardDto);
    }

    @PatchMapping("/board")
    public BoardEntity update(@RequestBody BoardDto boardDto) {
        return boardService.update(boardDto);
    }

    @DeleteMapping("/board/{no}")
    public void delete(@PathVariable(name = "no") Long no) {
        boardService.delete(no);
    }
}

 

4. 게시판 서비스

    패키지는 service 를 추가로 붙혀서 생성한다. 

@RequiredArgsConstructor
@Service
public class BoardService {

    private final MessageSource messageSource;
    private final BoardRepository boardRepository;
	
    /**
     * 게시글 목록 가져오기
     */
    public List<BoardDto> getBoardList() {
        List<BoardEntity> boardEntities = boardRepository.findAll();
        List<BoardDto> dtos = new ArrayList<>();

        for (BoardEntity entity : boardEntities) {
        	dtos.add(entityToDto(entity));
        }
        return dtos;
    }
    
    /**
     * 게시글 상세내용 조회
     */
    public BoardDto getBoard(Long no) {
    	BoardEntity entity = boardRepository.findById(no)
    			.orElseThrow(() -> new RuntimeException(messageSource.getMessage("error.notFound", new String[]{"게시글"}, Locale.getDefault())));
    	return entityToDto(entity);
    }
    
    /**
     * 게시글 등록
     */
    public BoardEntity create(BoardDto dto) {
    	return boardRepository.save(dtoToEntity(dto));
    }
    
    /**
     * 게시글 수정
     */
    public BoardEntity update(BoardDto dto) {
    	BoardEntity entity = boardRepository.findById(dto.getBoardNo())
    			.orElseThrow(() -> new RuntimeException(messageSource.getMessage("error.notFound", new String[]{"게시글"}, Locale.getDefault())));
    	entity.setTitle(dto.getTitle());
    	entity.setContent(dto.getContent());
    	entity.setWriter(dto.getWriter());
        entity.setPictureUrl(dto.getPictureUrl());
        entity.setUpdDate(LocalDateTime.now());
        
    	return boardRepository.save(entity);
    }

    /**
     * 게시글 삭제
     */
    public void delete(Long no) {
    	
    	BoardEntity entity = boardRepository.findById(no)
    			.orElseThrow(() -> new RuntimeException(messageSource.getMessage("error.notFound", new String[]{"게시글"}, Locale.getDefault())));
    	boardRepository.delete(entity);
    }

    /* 입력건 -> 테이블로 */
    private BoardEntity dtoToEntity(BoardDto dto) {
    	BoardEntity entity = BoardEntity.builder()
                .writer(dto.getWriter())
                .title(dto.getTitle())
                .content(dto.getContent())
                .pictureUrl(dto.getPictureUrl())
                .regDate(LocalDateTime.now())
                .build();
        return entity;
    }
    
    /* 테이블 데이터 -> 화면쪽으로 */
    private BoardDto entityToDto(BoardEntity entity) {
    	BoardDto dto = BoardDto.builder()
    			.boardNo(entity.getBoardNo())
                .writer(entity.getWriter())
                .title(entity.getTitle())
                .content(entity.getContent())
                .pictureUrl(entity.getPictureUrl())
                .regDate((entity.getRegDate() != null)? entity.getRegDate().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")):null)
                .updDate((entity.getUpdDate() != null)? entity.getUpdDate().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")):null)
                .build();
        return dto;
    }
}

위 서비스 소스에서 messageSource.getMessage("error.notFound", new String[]{"게시글"}, Locale.getDefault()) 를 적용한것이 보일 것이다.  여기서 메세지 처리 관련하여 간략히 집고 넘어가겠다.

Locale.getDefault() 은 messages.properties의 메시지를,
Locale.US 은 messages_en_US.properties의 메시지를,
Locale.KOREAN은 messages_ko_KR.properties의 메시지를 출력한다.
단, getDefault()에 맞는 Locale파일이 존재한다면 그게 우선이 된다.
만약, messages.propertie, messages_ko_KR.properties 두개가 있고 
현재 Locale이 ko, KR이라면 두번째 파일을 우선한다.

 

5. 게시판 레파지토리

     패키지는 repository 를 추가로 붙혀서 생성한다. 

public interface BoardRepository extends JpaRepository<BoardEntity, Long> {
	
}

 

6. 백엔드와의 통신을 위해서 axios 모듈을 프로젝트 폴더에서 npm을 이용하여 추가로 설치한다.

npm install --save axios

 

7. 설치된 axios를 공통으로 사용이 가능하도록  main.js에 전역변수로 지정한다.

import { createApp } from 'vue'
import App from './App.vue'
import router from './router/router.js'
import axios from 'axios'

const app = createApp(App)

app.config.globalProperties.$axios = axios;  //전역변수로 설정 컴포넌트에서 this.$axios 호출할 수 있음
app.config.globalProperties.$serverUrl = '//localhost:8081' //api server
app
  .use(router)
  .mount('#app')

 

8. BoardList.vue 에서 기존 임시 데이터를 제거하고 axios를 통해서 실제 테이블에서 데이터를 가져오는 소스로 바꾼다.

    mounted()와 methods: 부분을 수정한 내용이다.

  mounted() {
    this.fnGetList()
  },
  methods: {
    fnGetList() {
      this.requestBody = { // 데이터 전송
        page: this.page,
        size: this.size
      }

      this.$axios.get(this.$serverUrl + "/board/list", {
        params: this.requestBody,
        headers: {}
      }).then((res) => {      

        this.list = res.data

      }).catch((err) => {
        if (err.message.indexOf('Network Error') > -1) {
          alert('네트워크가 원활하지 않습니다.\n잠시 후 다시 시도해주세요.')
        }
      })
    }
  }

 

 수정이 완료되면 화면에서 게시판 링크를 클릭하면 아래와 같은 화면이 조회된다.

20개 리스트가 보일 것이다. 그럼 성공이다.

 

반응형

프로그램/Vue.js Related Articles

MORE

Comments