생성, 조회, 삭제, 수정 API 생성하기

Time Saver 2020. 11. 16. 17:03

github.com/bang-gui/blog

 

bang-gui/blog

나의 리액트 페이지. Contribute to bang-gui/blog development by creating an account on GitHub.

github.com


API를 작성하려면 URL지정에 신경 써야 한다.

현재 우리는 아래의 url 중에 post만을 구현하였다.

종류 기능
POST /posts 포스트 작성
GET /posts 포스트 목록 조회
GET /posts/:id 특정 포스트 조회
DELETE /posts/:id 특정 포스트 삭제
PATCH /post/:id 특정 포스트 업데이트
POST /posts/:id/comments 특정 포스트에 댓글 등록
GET /posts/:id/comments 특정 포스트의 댓글 목록 조회
DELETE /posts/:id/comments/:commentsId 특정 포스트에 특정 댓글 삭제

정말 감사하게도 댓글 기능을 구현하기 위한 URL을 책에서 알려준다.

위의 표대로 댓글을 위한 라우터를 먼저 만들어 보자.

 

import Router from 'koa-router';
import comments from '../comment';
import * as postsCtrl from './posts.ctrl';
import checkLoggedIn from '../../lib/checkLoggedIn';


const posts = new Router();

posts.get('/', postsCtrl.list);
posts.post('/', checkLoggedIn, postsCtrl.write);

const post = new Router(); // /api/posts/:id
post.get('/', postsCtrl.read);
post.delete('/', checkLoggedIn, postsCtrl.checkOwnPost, postsCtrl.remove);
post.patch('/', checkLoggedIn, postsCtrl.checkOwnPost, postsCtrl.update);

post.use('/comments',comments.routes()); //:id가 붙어있는 포스트에만 적용한다.


posts.use('/:id', postsCtrl.getPostById, post.routes());


export default posts;

 

 

import Router from 'koa-router';

const comments = new Router();

comments.post('/',ctx =>{
    ctx.body = '댓글 포스트'
});
comments.get('/',ctx =>{
    ctx.body = '댓글 겟'
});
comments.delete('/:commentsId',ctx =>{
    ctx.body = '댓글 델리트'
})


export default comments;

포스트맨으로 시험해보았다. 잘 동작한다.

 

파일 트리

 

라우트 경로는 잘 지정했다.

글쓰기 기능을 시험하려면 인풋이 마련되어야 한다.

포스트 뷰어 하단에 댓글을 작성할 수 있는 인풋과 버튼을 만들어 보겠다. 

전체 구성은 그림과 같다

그림이 최고다

컨테이너로 코멘트 뷰어를 감싸고 데이터와 액션들을 마련할 것이다.

import { createAction, handleActions } from 'redux-actions';

const CHANGE_INPUT = 'comments/CHANGE_INPUT';

export const changeInput = createAction(CHANGE_INPUT, input => input);

const initialState = {
  input: '',
};

const comments = handleActions(
  {
    [CHANGE_INPUT]: (state, { payload: input }) => {
      console.log(input);
      return {
        ...state,
        input: input,
      };
    }
  },
  initialState,
);

export default comments;

 

아주 간단한 리덕스 모듈을 먼저 작성했다.

리덕스를 적용시키는 것은 책에 잘 나와있으니 책을 참고하길 바란다.

 

import React, { useCallback } from 'react';
import { useSelector, useDispatch } from 'react-redux';
import { changeInput } from '../../modules/comments';
import CommentsViewer from '../../components/comments/CommentsViewer';

const CommentsViewerContainer = () => {
  const input = useSelector((state) => state.comments.input);
  const dispatch = useDispatch();

  const onChangeCommentInput = useCallback(input =>
    dispatch(changeInput(input)),
    [dispatch]
  );

  return (
      <CommentsViewer
        onChangeCommentInput={onChangeCommentInput}
        input={input}
      />
  );
};

export default CommentsViewerContainer;

컨테이너를 마련하고...

import React from 'react';
import styled from 'styled-components';
// import palette from '../../lib/styles/palette';
import Responsive from '../common/Responsive';
import CommentInput from '../comments/CommentInput';
import Button from '../../components/common/Button';

const CommentsViewerBlock = styled(Responsive)`
  margin-top: 4rem;
`;

const CommentButtonBlock = styled.div`
  margin-top: 1rem;
  display: flex;
  justify-content: flex-end;
`;

const CommentsViewer = ({ onChangeCommentInput, input }) => {
  return (
    <CommentsViewerBlock>
      <CommentInput onChangeCommentInput={onChangeCommentInput} input={input} />
      <CommentButtonBlock>
        <Button>댓글 작성</Button>
      </CommentButtonBlock>
    </CommentsViewerBlock>
  );
};

export default CommentsViewer;

 

뷰어를 만들고...

 

import React from 'react';
import styled from 'styled-components';
import TextareaAutosize from 'react-autosize-textarea';

const StyledCommentInput = styled.input`
  width: 100%;
  border: none;
  outline: none;
  font-size: 1rem;
  padding: 1rem;
  border: 1px solid gray;
  border-radius: 4px;
  color: gray;
  display: block;
  line-height: 1.5;
`;
const CommentInput = ( {onChangeCommentInput, input }) => {
  const onChange = (e) => onChangeCommentInput(e.target.value);
  return (
    <>
      <hr />
      <StyledCommentInput
        value={input}
        onChange={onChange}
        placeholder="댓글을 입력하세요"
        rows={2}
        maxRows={20}
      />
    </>
  );
};

export default CommentInput;

 

그리고 인풋을 만들었다.

 

그리고 마지막으로 컨테이너를 postViewer 컴포넌트 안에 위치해주어야 한다.

import React from 'react';
import styled from 'styled-components';
import palette from '../../lib/styles/palette';
import Responsive from '../common/Responsive';
import SubInfo from '../common/SubInfo';
import Tags from '../common/Tags';
import CommentsViewerContainer from '../../containers/comments/CommentsViewerContainer'
import { Helmet } from 'react-helmet-async';

const PostViewerBlock = styled(Responsive)`
  margin-top: 4rem;
`;
const PostHead = styled.div`
  border-bottom: 1px solid ${palette.gray[2]};
  padding-bottom: 3rem;
  margin-bottom: 3rem;
  h1 {
    font-size: 3rem;
    line-height: 1.5;
    margin: 0;
  }
`;

const PostContent = styled.div`
  font-size: 1.3125rem;
  color: ${palette.gray[8]};
`;

const PostViewer = ({ post, error, loading, actionButtons }) => {
  // 에러 발생 시
  if (error) {
    if (error.response && error.response.status === 404) {
      return <PostViewerBlock>존재하지 않는 포스트입니다.</PostViewerBlock>;
    }
    return <PostViewerBlock>오류 발생!</PostViewerBlock>;
  }

  // 로딩중이거나, 아직 포스트 데이터가 없을 시
  if (loading || !post) {
    return null;
  }

  const { title, body, author, publishedDate, tags } = post;
  return (
    <PostViewerBlock>
      <Helmet>
        <title>{title} - Received King</title>
      </Helmet>

      <PostHead>
        <h1>{title}</h1>
        <SubInfo
          username={author.username}
          publishedDate={publishedDate}
          hasMarginTop
        />
        <Tags tags={tags} />
      </PostHead>
      {actionButtons}
      <PostContent dangerouslySetInnerHTML={{ __html: body }} />
      <CommentsViewerContainer/> // 여기에다 요로콤 추가해라
    </PostViewerBlock>
  );
};

export default PostViewer;

 

댓글 input완성!!

자... 이제 뭘 해야 하나...

아 이제 쓰기 API를 만들어보자... 젠장...

 

댓글 POST 포스트맨

나는 댓글의 내용을 body라고 설정했다.

 

lib/api 아래에 comments.js를 만들자

import client from './client';

export const writeComments = ({ id, body }) =>
  client.post(`/api/posts/${id}/comments`, { body });

 

조금씩 간단하게, 쓰기만 먼저 구현해보자.

이제 위의 api를 사용하기 위한 리덕스 액션과 사가를 준비해보자...

이것만 해도 어마무시하게 복잡한 느낌이 든다.