OscBoard Demo 어플리케이션.(front(react) + back(spring)

carlkim·2023년 10월 19일
0

• 1. 소스코드
• 2. 데이터 베이스(Mysql)
• 3. FrontEnd(React.js)
• 3. Backend
o 3-1 CONTROLLER
o 3-2 DTO
o 3-3 ENTITY
o 3-4 Repository
o 3-5 Service
o 3-6 application.yml

1. 소스코드

React.js 소스 코드
https://github.com/eternityhwan/osc-board-front
도커 이미지
https://hub.docker.com/repository/docker/mirrorkyh/oscboardfront
SpringBoot 소스 코드
https://github.com/eternityhwan/osc-board-back
도커 이미지
https://hub.docker.com/repository/docker/mirrorkyh/oscboard

2. 데이터 베이스(Mysql)

  • docker-compose 를 사용하여 개인 AWS EC2 인스턴스에 설치 완료

  1. FrontEnd(React.js)
  • React.js 라이브러리 활용.
  • 부트스트랩 사용 없이 헤더, 보디, 푸터 레이아웃 구성.
  • Flex 박스 중첩을 활용하여 제작.
  • Axios를 활용하여 Get, Post, Patch, Delete 구현.
  • React-router-dom 을 활용하여 Route 기능 실현
    좌측 박스에 글 목록, 글 작성, 회원 가입, 로그인을 클릭 시 해당 컴포넌트를
    우측 박스에 출력.
  • 리스트에서 제목을 클릭 하면 해당 글의 파라메터를 받아
    글을 수정, 삭제 할 수 있는 페이지로 이동하게 하게 하였습니다.


4. Backend

  • Springboot 사용
  • @RestController 활용, JSON 데이터를 전송하게 하였습니다.
  • API → DTO → ENTITY → SERVICES → REPOSITORY → JPA → DB 형태로 구현 하였습니다.
  • 테스트는 Talend API tester 라는 크롬 확장 프로그램으로 진행하였습니다

3-1 CONTROLLER

@RestController
@Slf4j
@CrossOrigin(origins = "*") // 프론트앤드와 통신 CORS 문제 해결
@Tag(name = "게시판", description = "게시판")
public class BoardApiController {

    @Autowired // DI 생성 객체를 가져와 연결.
    private ArticleService articleService;
    
    // FindAll Get
    @Operation(summary = "게시물 전체건수 조회", description = "게시물 전체건수를 조회합니다.")
    @GetMapping(value = "/api/boards")
    public List<Article> index() {
        return articleService.index();
    }

    // Get by id
    @Operation(summary = "게시물 개별건수 조회", description = "게시물 개별건수를 조회합니다.")
    @GetMapping(value = "/api/boards/{id}")
    public Article search(@PathVariable Long id) {
        return articleService.search(id);
    }

    // POST
    // @RequsestBody로 Dto 클래스를 받는다
    @Operation(summary = "게시물 작성 ", description = "게시물을 작성합니다.")
    @PostMapping(value = "/api/boards/posts")
    public ResponseEntity<Article> post(@RequestBody ArticleDto dto) {
        Article posts = articleService.dbPosts(dto);
        return (posts != null) ?
                ResponseEntity.status(HttpStatus.OK).body(posts) :
                ResponseEntity.status(HttpStatus.BAD_REQUEST).build();
    }

    // PATCH
    @Operation(summary = "개별 게시믈 수정", description = "게시믈 수정합니다.")
    @PatchMapping(value = "/api/boardsR/{id}")
    public ResponseEntity<Article> update(
            @PathVariable Long id,
            @RequestBody ArticleDto dto) {
        // 서비스에게 revision 메소드를 시킨다(파라메터 id와 dto를 넘겨준다)
        // updated 객체로 넘겨준다.
        // Controller는 Controller의 코드만 가지고 있는다.
        // 뭘 받고 뭘 리턴하는지만 알면 된다.
        Article updated = articleService.revision(id, dto);
        return (updated != null) ?
                ResponseEntity.status(HttpStatus.OK).body(updated) :
                ResponseEntity.status(HttpStatus.BAD_REQUEST).build();
        // 굿 요청 ResponseEntity 상태 ok를 넣어주고 body에 update 객체를 반환해준다.
        // 배드 요청 ResponseEntity 상태 bad를 넣고 몸통없이 빌드해서 보여주면된다.
    }

    // DELETE
    @Operation(summary = "게시믈 삭제", description = "게시믈을 삭제합니다.")
    @DeleteMapping(value = "/api/boardsD/{id}")
    public ResponseEntity<Void> delete(@PathVariable Long id) {
        Article deleted = articleService.delete(id);
        return (deleted != null) ?
                ResponseEntity.status(HttpStatus.NO_CONTENT).build() :
                ResponseEntity.status(HttpStatus.BAD_REQUEST).build();
    }
}

3-2 DTO

@AllArgsConstructor
@ToString
public class ArticleDto {

    private Long id;
    private String title;
    private String content;
    
    public Article toEntity() {
        return new Article(id, title, content);
        // Entity 클래스의 객체 Article 선언
    }
}

3-3 ENTITY

@Entity //  엔티티 어노테이션을 붙여줘야 객체를 인식한다.
@AllArgsConstructor
@NoArgsConstructor
@ToString
@Getter
public class Article {

    @Id // 대표값을 지정 고유의 값
    @GeneratedValue // 자동 생성
    private Long id;

    @Column // DB에서 인식하게 컬럼을 붙여준다 세로줄.
    private String title;

    @Column
    private String content;


    public void patch(Article article) {
        if ( article.title != null)
            this.title = article.title;
        if (article.content != null)
            this.content = article.content;
        }
    }

3-4 Repository
JPA 활용을 위해 CrudRepository 사용하였고
findall 의 반환 타입을 수정하였습니다.

@Repository
public interface ArticleRepository extends CrudRepository<Article, Long> {
    // <관리대상엔티티, 대표값의 타입(Article의 대표값 id의 타입은 long)> 입력


    @Override
    List<Article> findAll();
}

3-5 Service

@Service // 서비스 선언 (서비스 객체를 스프링부트에 생성)
@Slf4j
public class ArticleService {

    @Autowired // 의존성 주입, 서비스는 레파지토리만 바라본다.
    private ArticleRepository articleRepository;

    // get all
    public List<Article> index() {
        return articleRepository.findAll();
    }
    // get id
    public Article search(Long id) {
        return articleRepository.findById(id).orElse(null);
    }

    // post
    public Article dbPosts(ArticleDto dto) {
        // 1. dto를 받았으면 dto를 entity로 바꾸고
        // 1-1.Entity article 객체로 바꾼다
       Article article = dto.toEntity();
       // article가 null이 아니면 null을 반환해라
        // 수정이 안되게 아이디가 존재한다면 null을 반환해라 명령
       if (article.getId() != null) {
           return null;
       }
       // Entity 객체인 article 객체롤 DB에 저장하면된다
       return articleRepository.save(article);
    }


    public Article revision(Long id, ArticleDto dto) {
        //  1. 수정용 엔티티 생성
        Article article = dto.toEntity();
        log.info("id: {}, article: {}", id, article.toString());

        // 2. 대상 엔티티를 조회
        Article targetEntity = articleRepository.findById(id).orElse(null);

        // 3. 잘못된 요청 처리 (대상이 없거나, id가 다른경우)
        if (targetEntity == null || id != article.getId()) {
           log.info("잘못된 요청 : id: {}, article: {}", id, article.toString());
           return null;
        }
        // 4. 업데이트
        targetEntity.patch(article);
        Article updated = articleRepository.save(targetEntity);
        return updated;
    }


    public Article delete(Long id) {

        // 1. 대상 찾기
        Article targetEntity = articleRepository.findById(id).orElse(null);

        // 1-1 잘못된 요청 처리
        if (targetEntity == null) {
            return null;
        }

        // 2. 대상 삭제
        articleRepository.delete(targetEntity);
        return targetEntity;
    }

    
    }
}

3-6 application.yml

# MySQL8 설정
spring:
  datasource :
    driver-class-name : com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://ec2-3-38-111-117.ap-northeast-2.compute.amazonaws.com:3306/oscboard?useSSL=false&characterEncoding=UTF-8&serverTimezone=UTC
    username: root
    password: 1q2w3e4r

# 콘솔에 SQL 출력 여부
  jpa:
    show-sql: true
    database-platform: org.hibernate.dialect.MySQL8Dialect
# hibernate 설정
    database : mysql
    hibernate.ddl-auto : update
    hibernate.naming.strategy : org.hibernate.cfg.ImprovedNamingStrategy
    hibernate.naming.physical-strategy : org.hibernate.boot.model.naming.PhysicalNamingStrategyStandardImpl
    generate-ddl : false
    properties.hibernate.format_sql : true
    properties.hibernate.enable_lazy_load_no_trans : true

느낀점.
1. 과제를 최대한 쉬운 방법으로 진행할 수 있게 배려 주신 이사님에게 감사드립니다.
2. 충분한 시간이었으나 여유롭지 않았습니다.
3. 코드 작성하면서 다시 한번 아는 것을 복습하는 기회가 되었습니다.
4. 구현은 하였지만 문서로 정리하면서 더 구체적으로 복기하는 기회를 주셨으면 합니다.

profile
기본부터 가면 됩니다.

0개의 댓글