블로그만들기 (4)

reactjs 프론트와 java 백엔드 연동

이번에는 ReactJS로 구현한 프론트엔드와 Java + 스프링부트로 구현한 백엔드를 연동해보았습니다.

react를 쓰니 일반적인 자바스크립트 파일을 연결하는 것도 잘 모르겠고, ajax 호출을 jquery로만 했었는데 fetch 메서드를 쓰는 것이 공식문서의 예제로 나와있어서 그걸 이용했습니다. 

백엔드에 CORS 설정도 빼먹었었고요...

일단 화면에서 바뀐 점은 Blog > 다이어리 메뉴를 선택하면 검색을 위한 입력창과 버튼, 새글 등록을 위한 버튼을 넣었고(기능은 아직 구현못했네요), 리스트 목록 조회하는 부분만 간단하게 연결해놓았습니다. 소스코드는 많이 수정했는데 실제 화면에서 바뀐건 뭐 없네요 ㅡㅡㅋ

 

게시글 조회 기능 구현

 

앞으로 할 일들을 나열해봤습니다.

  • 등록기능 프론트 구현 및 백엔드와 연동
  • 검색기능 백엔드 구현 및 프론트와 연동
  • 목록의 헤더에 제목, 내용, 수정일 표시
  • 내용은 한 줄을 넘기지 않도록 일부만 출력되도록 수정
  • 등록일시 표시 -> 백엔드 수정
  • 내부적으로는 수정일시도 관리
  • 게시글 좌측에 게시글 번호 넣기 -> 몽고DB에 auto increment 기능이 없어 소스레벨에서 구현해야함

역시 프론트와 백엔드를 혼자 하려니 할 일이 너무 많네요 ㅜㅜ

그래도 재미있게 만들어 가고 있습니다.

요새 티스토리가 자체광고를 붙여서 광고가 자꾸 뜨는것 같네요...로그인할 때도 뜨고...

빨리 블로그 앱 만들어서 라이브해서 직접 사용해보고 싶네요

 

 

참고문서:

스프링부트 CORS 설정하기 >> spring.io/guides/gs/rest-service-cors/#global-cors-configuration

 

Enabling Cross Origin Requests for a RESTful Web Service

this guide is designed to get you productive as quickly as possible and using the latest Spring project releases and techniques as recommended by the Spring team

spring.io

리액트 ajax 호출하기 >> reactjs.org/docs/faq-ajax.html

 

AJAX and APIs – React

A JavaScript library for building user interfaces

reactjs.org

리액트 부모상태 업데이트하기 >> stackoverflow.com/questions/35537229/how-to-update-parents-state-in-react

 

How to update parent's state in React?

My structure looks as follows: Component 1 - |- Component 2 - - |- Component 4 - - - |- Component 5 Component 3 Component 3 should display some data depending on state of Component 5....

stackoverflow.com

 

이번 프로젝트에서는 Java + SpringBoot + Mongo DB 로 구현할 예정입니다.

각각 사용할 버전은 다음과 같습니다.

 

- Java 8 (오라클jdk가 상용목적으로는 유료화되어 추후 코틀린으로 변경하는 프로젝트를 진행해봐야겠네요)

- Springboot 2.4.0 (20년 11월 현재 최신버전)

- Mongo DB 4.4.2 Community Server (20년 11월 현재 최신버전)

 

몽고디비 설치 관련해서는 몽고DB 최신버전 설치하기 포스팅을 참고하시면 됩니다.

설치 후 사용자 계정 생성 및 데이터 베이스 생성 관련해서는 [몽고DB] 기본명령어 포스팅을 참고해주세요.

 

이제 그래들 자바 프로젝트를 하나 생성하여 Blog > 다이어리 메뉴에서 사용할 CRUD를 순서대로 작성해보겠습니다.

가장 먼저 프로젝트 구성을 한번 살펴보겠습니다.

블로그 만들기 백엔드 프로젝트

 

우선 build.gralde파일을 아래와 같이 작성했습니다.

plugins {
    id "org.springframework.boot" version "2.4.0"
    id 'java'
}

group 'com.keichee'
version '1.0-SNAPSHOT'
sourceCompatibility = "1.8"

repositories {
    jcenter()
}

ext {
    springVersion = '2.4.0'
}

dependencies {
    testImplementation group: 'junit', name: 'junit', version: '4.12'

    implementation 'org.mongodb:mongodb-driver-sync:4.1.1'
    testImplementation group: 'ch.qos.logback', name: 'logback-classic', version: '1.2.3'

    implementation "org.springframework.boot:spring-boot-starter-web:$springVersion"
    testImplementation("org.springframework.boot:spring-boot-starter-test:$springVersion") {
        exclude group: 'org.junit.vintage', module: 'junit-vintage-engine'
    }

    compileOnly 'org.projectlombok:lombok:1.18.16'
    annotationProcessor 'org.projectlombok:lombok:1.18.16'

    testCompileOnly 'org.projectlombok:lombok:1.18.16'
    testAnnotationProcessor 'org.projectlombok:lombok:1.18.16'

    implementation "io.springfox:springfox-boot-starter:3.0.0"
    implementation 'io.springfox:springfox-swagger-ui:3.0.0'

    implementation 'com.google.guava:guava:30.0-jre'
}

test {
    useJUnitPlatform()
}

 

mongodb-driversync 4.1.1 을 사용했습니다.

로깅을 위해서 logback-classic을 추가했고 

api 를 쉽게 만들 수 있도록 spring-boot-starter-web을 추가

getter, setter 등의 어노테이션 사용을 위해 lombok 라이브러리를 추가하였고

스웨거 문서를 사용하려고 springfox 라이브러리들도 추가했습니다.

 

이제 몽고DB와 연결을 위한 설정을 아래와 같이 해줍니다.

@Configuration
public class MongoConfig {

    private static final String host = "localhost";
    private static final int port = 27017;
    private static final String database = "blogapp";

    @Bean
    public MongoDatabase blogAppDatabase() {
        MongoClient client = MongoClients.create(
                MongoClientSettings.builder()
                        .applyToClusterSettings(builder ->
                                builder.hosts(Collections.singletonList(new ServerAddress(host, port))))
                        .build());
        return client.getDatabase(database);
    }
}

 

컨트롤러에는 딱 4개의 Restful API를 만들었습니다.

@Slf4j
@RestController
@RequestMapping("/diary/life")
@AllArgsConstructor
public class DiaryLifeController {

    private final DiaryLifeService diaryLifeService;

    @ApiOperation("전체 목록 조회")
    @GetMapping
    public Response<List<Post>> getPosts() {
        log.info("전체 목록 조회");

        return new Response<>(diaryLifeService.getPosts(null));
    }

    @ApiOperation("다이어리 포스팅 신규생성")
    @PostMapping
    public Response<Void> savePost(@RequestBody Post post) {
        validatePostInput(post);
        diaryLifeService.createPost(post);
        return new Response<>();
    }

    @ApiOperation("다이어리 포스팅 업데이트")
    @PutMapping
    public Response<Void> updatePost(@RequestBody Post post) {
        validatePostInput(post);
        diaryLifeService.updatePost(post);
        return new Response<>();
    }

    @ApiOperation("다이어리 포스팅 삭제")
    @DeleteMapping
    public Response<Void> deletePost(@RequestParam String _id) {
        diaryLifeService.deletePost(_id);
        return new Response<>();
    }

    private void validatePostInput(Post post) {
        if (ObjectUtils.isEmpty(post.getTitle())) {
            throw new BadRequestException("No TITLE exists.");
        }
        if (ObjectUtils.isEmpty(post.getContent())) {
            throw new BadRequestException("No CONTENT exists.");
        }
    }

    @ExceptionHandler
    public ResponseEntity<Response<String>> badRequest(BadRequestException e) {
        log.error("Bad request.., e-msg:{}", e.getMessage(), e);
        return ResponseEntity.badRequest().body(new Response<>(e.getMessage()));
    }
}

 

스웨거로 보면 다음처럼 나오게 됩니다.

다이어리 API 스웨거

 

스웨거를 이용해서 기능 테스트를 한번 해보겠습니다.

 

 

 

현재 구현된 기능은 모두 정상적으로 동작하는 것 까지 확인했습니다.

 

여기까지 작업하면서 쉽게 풀리지 않았던 부분은 몽고DB를 처음 사용하다보니 몽고DB 클라이언트를 이용한 CRUD만드는 부분이었습니다. 기존 RDS와는 다르게 ObjectID 로 핸들링을 해야하는 부분이 있었고, auto increment pk 설정을 따로 할 수 없었습니다. 만약 게시글의 번호가 필요하다면 어떻게 데이터를 저장해야할지 고민이 좀 되는 부분입니다. 각 게시글마다 시퀀스값을 넣어줘야하는데 구글링해서 나오는 것들은 synchronized 가 안될 것 처럼 보여서 테스트도 좀 해봐야 할 것 같습니다.

 

여기까지 백엔드의 기본적인 구현을 완료했습니다.

다음 포스팅에서는 프론트와 백엔드를 연결하는 부분에 대해서 올리겠습니다.

우선 화면 기획부터 시작했다.

퇴근 길 지하철에서 한시간 동안 갤노트에다가 끄적여봤다.

워낙 꼼꼼한 스타일이라 이것저것 디테일한 기능들을 다 적어넣고 싶었지만 그렇게 시작하면 힘들어서 중도포기하게 될 것 같아 계속 드는 생각들을 뿌리치고 간단하게만 끄적였다.

갤럭시노트의 S펜을 이용한 메모가 처음인지라 서투르게 작성했다 ㅎ

메인화면은 크게 상단부(헤더부분과 최상위메뉴)와 하단부(사이드메뉴 + 컨텐트)로 구성하였다.

컨텐트 영역은 좌측에 사이드메뉴가 있을수도 있고 없을수도 있으며 카드형 목록보가와 리스트형을 지원할 수 있도록 할 계획이다.

최상위 메뉴는 주인장 또는 블로그의 소개(Intro), 블로그(Blog), 그리고 분석/통계(Stats) 로 구성했다.
블로그메뉴의 하위에는 개발일지, 다이어리, 제품리뷰, 여행정보 메뉴가있고, 그리고 마지막으로 분석통계 메뉴에는 각종 통계자료를 공유할 생각이다.

그렇게 만들어본 화면은 아래와 같다.

 

이제 각 메뉴별 화면 기획 및 백엔드 개발을 시작하면 될 것 같다.

우선 데이터베이스는 AWS document DB에서 지원하는 mongo DB를 써볼 예정이다.

한 row에 대해서 총 16MB 까지 데이터 저장이 가능하니 이미지를 등록하는 게시글에 대해서도 하나의 row에 저장을 할 수 있을 것 같다. 

무료로 mongoDB 클라우드 서비스를 사용할 수도 있지만 이런저런 예기치 못한 제약사항이 생길 수도 있을 것 같아 커뮤니티 버전을 다운로드 받아서 사용할 예정이다.

백엔드는.....역시 제일 빨리 할 수 있는 스프링부트기반의 자바로 가야겠다.. 시간단축을 위해서...

 

블로그 웹앱 만들기 개인프로젝트

티스토리 블로그를 사용한지도 몇 년이 된 것 같다.

처음 시작은 구글 광고를 붙여서 광고수익을 얻을 생각으로 시작했었는데

이직을 하고 대규모 앱을 거의 혼자서 유지보수 및 신규개발을 하면서 너무 바빠서 글을 쓸 시간이 없었다.

그렇게 2년이 넘는 시간이 훌쩍 흘렀다.

이직한 회사에서 관리자 웹앱을 혼자 관리하게 되면서 평소 풀스택이 되고싶었던 꿈이 다시 살아나기 시작했고

몇 일 전부터 리액트 공부를 시작했다.

생활코딩의 리액트 동영상도 보고, 리덕스 강좌도 보고...

하지만 역시 보기만 하는건 큰 도움이 되지 않기에 직접 코딩을 해가면서 공부해야겠다는 생각이 들었고

어떤 프로젝트를 해볼까 하다가 개인 블로그를 직접 만들어야겠다는 생각이 들었다.

티스토리를 사용하면 다른 블로그들보다 커스터마이징할 수 있는 부분이 많아 좋긴하지만 그래도 제약사항이 있을 수 밖에 없다.

그리고 티스토리가 서비스 중지 선언을 해버리면? ㅠㅠ

그럼 작성한 글들도 모두 사라지게 될테니 말이다. 

설령 작성한 글들을 다운로드 받을 수 있게 해준다고 해도 포맷을 어떻게 제공하느냐에 따라 다른 블로그에 옮겨심기도 불편할 수 밖에 없다.

그래서 이번 기회에 개인 블로그를 직접 만들어서 도메인 연동도 하고 꾸준히 잘 가꾸어 나가야 겠다는 생각을 했다.

그리고 블로그를 완성하기 까지의 과정을 이곳에 남겨두기로 했다.

 

그렇게 백엔드 개발자의 리액트로 개인블로그 만들기는 시작이 되었다...

 

해야할 일은 많다. 서버는 어떻게 구성할 것이며 어떤 클라우드 서비스를 이용할 것인지 프로젝트 구성은 어떻게 할 것인지 등등...

하지만 이런것들을 상세하게 다 따져가면서 하기에는 시간이 오래걸리니 리액트로 화면구성하는 것에 일단 집중하기로 했다.

화면을 구성하는 것은 여러 사이트를 돌아다니면서 벤치마킹하면서 해야겠다

백엔드는 늘 해오던 자바와 스프링 부트를 이용할지...예전에 해봤던 노드를 다시 써볼지...파이썬을 써볼지 고민이 좀 되는데...

자바+스프링부트는 업으로 하고있는 스펙이다보니 빠른 시간내에 만들 수 있는 반면 리액트와 함께 사용하려면 프로젝트를 분리해서 관리해야할 것 같고 노드서버와 자바서버를 띄워야 완전체가 된다. 

노드를 쓰면 프론트와 백엔드 프로젝트를 굳이 분리할 필요가 없고 서버도 노드서버만 띄우면 되기 때문에 개인용으로 관리하기에는 더 편하긴 하지만, 익숙하지 않은 노드를 쓰자니 좀 부담이 되긴한다.

 

일단은 리액트를 이용해서 껍데기(프론트)부터 만들어놓고 고민해야겠다.