0329 - 0404

0329

게시글 목록 조회 API

게시글을 목록으로 받아오기 위해 API를 작성하였다.

  • 게시글을 목록으로 조회하기 위해서는 기본적으로 paging이 필요했다.
  • paging을 구현하는 데에는 여러 방법이 있지만, 그 중에서 프론트 측에서 페이지 번호와 페이지 당 나눌 개수를 지정해주면 그에 따른 데이터만 반환해주는 방식으로 구현하였다.

Criteria

public class Criteria {

    // 현재 페이지 번호
    private int currentPageNo;

    // 페이지당 출력할 데이터 개수
    private int recordsPerPage;

    // 검색어
    private String searchWord;

    public Criteria () {
        this.currentPageNo = 1;
        this.recordsPerPage = 10;
    }

    public int getStartPage() {
        return (currentPageNo - 1) * recordsPerPage;
    }
}
  • 페이징 기준으로 사용할 criteria를 도메인 패키지 내부에 작성하였다.
  • 매개변수로 현재 페이지 번호와 페이지당 출력할 데이터 개수를 지정하지 않아도 첫 번째 페이지에 10개씩 출력하기 위해서 생성자를 작성하였다.
  • getStartPage는 currentPageNo과 recordsPerPage를 가지고 어디서부터 목록을 출력할 것인지 지정해주는 getter 함수이다.

컨트롤러 - 서비스 - 매퍼

// Controller
/**
* 게시글 목록 조회
* @param criteria
* @return
*/
@GetMapping("/lists")
public BaseResponse getBoardList(@ModelAttribute Criteria criteria) {

    // 리턴 값의 순서를 보장하기 위해 LinkedHashMap으로 구현
    Map<String, Object> data = new LinkedHashMap<>();

    // 페이지 당 레코드 개수와, 해당 페이지 번호 정보를 받아서 페이징 처리를 하여 목록 조회
    Map<String, Object> result = boardService.getBoardList(criteria);

    data.put("currentPageNo", criteria.getCurrentPageNo());
    data.put("recordsPerPage", criteria.getRecordsPerPage());
    data.put("totalCnt", result.get("totalCnt"));
    data.put("list", result.get("list"));
    return new BaseResponse(data);
}

// ServiceImpl
@Override
public Map<String, Object> getBoardList(Criteria criteria) {
    Map<String, Object> result = new HashMap<>();

    List<Board> list = boardMapper.getBoardList(criteria);
    int totalCnt = boardMapper.getTotalCnt();

    result.put("totalCnt", totalCnt);
    result.put("list", list);

    return result;
}

// Mapper
List<Board> getBoardList(Criteria criteria);

int getTotalCnt();

mybatis query

<!-- 게시글 목록 조회 -->
<select id="getBoardList" parameterType="Criteria" resultType="Board">
    SELECT
        B.BOARD_NO,
        A.MEMBER_NO,
        A.MEMBER_NAME,
        B.TITLE,
        B.CONTENT,
        B.COMMENT_CNT,
        B.LIKE_CNT,
        B.REG_DATE
    FROM MEMBER A INNER JOIN BOARD B ON A.MEMBER_NO = B.MEMBER_NO
    ORDER BY B.BOARD_NO DESC
    LIMIT #{recordsPerPage} OFFSET #{startPage}
</select>

<!-- 전체 게시글 개수 반환 -->
<select id="getTotalCnt" resultType="int">
    SELECT COUNT(*)
    FROM BOARD
</select>
  • getBoardList 쿼리는 매개변수로 Criteria를 받아서 여러 개의 Board 레코드를 반환한다.
  • ORDER BY DESC를 사용하여 최신 순으로 정렬하였다.
  • OFFSET은 출력을 시작할 위치, LIMIT은 반환할 개수를 지정한다.
  • 여러 개의 레코드를 List 타입으로 받으면 하나의 레코드가 각각 add된다.
  • 총 목록의 개수도 조회하여 반환할 map에 삽입한다.

0330

게시글 검색 목록 API

검색 기능을 넣기 위해 검색어를 입력하면 제목, 내용에서 같은 단어가 있는 게시글만 목록으로 반환할 수 있도록 페이징 처리하여 구현하였다.

컨트롤러 - 서비스 - 매퍼

// Controller
/**
* 게시글 검색
* @param criteria
* @return
*/
@GetMapping("/searchLists")
public BaseResponse getSearchList(@ModelAttribute Criteria criteria) {

    // 리턴 값의 순서를 보장하기 위해 LinkedHashMap으로 구현
    Map<String, Object> data = new LinkedHashMap<>();

    Map<String, Object> result = boardService.getSearchList(criteria);

    data.put("searchWord", criteria.getSearchWord());
    data.put("currentPageNo", criteria.getCurrentPageNo());
    data.put("recordsPerPage", criteria.getRecordsPerPage());
    data.put("totalCnt", result.get("totalCnt"));
    data.put("list", result.get("list"));

    return new BaseResponse(data);
}

// Service
@Override
public Map<String, Object> getSearchList(Criteria criteria) {
    Map<String, Object> result = new HashMap<>();
    List<Board> list = new ArrayList<>();

    // 검색어가 없는 경우에는 빈 리스트와 개수 반환
    if (ObjectUtils.isEmpty(criteria.getSearchWord())) {
        result.put("totalCnt", 0);
        result.put("list", list);

        return result;
    }

    list = boardMapper.getSearchList(criteria);
    int totalCnt = boardMapper.getSearchTotalCnt(criteria.getSearchWord());

    result.put("totalCnt", totalCnt);
    result.put("list", list);

    return result;
}

// Mapper
List<Board> getSearchList(Criteria criteria);

int getSearchTotalCnt(String searchWord);

mybatis query

<!-- 검색어로 게시글 목록 조회 -->
<select id="getSearchList" parameterType="Criteria" resultType="Board">
    SELECT
        B.BOARD_NO,
        A.MEMBER_NO,
        A.MEMBER_NAME,
        B.TITLE,
        B.CONTENT,
        B.COMMENT_CNT,
        B.LIKE_CNT,
        B.REG_DATE
    FROM MEMBER A INNER JOIN BOARD B ON A.MEMBER_NO = B.MEMBER_NO
    WHERE
        title LIKE CONCAT('%', #{searchWord}, '%') OR
        content LIKE CONCAT('%', #{searchWord}, '%')
    ORDER BY B.BOARD_NO DESC
    LIMIT #{recordsPerPage} OFFSET #{startPage}
</select>

<!-- 검색어로 조회된 전체 게시글 개수 반환 -->
<select id="getSearchTotalCnt" parameterType="String" resultType="int">
    SELECT COUNT(*)
    FROM BOARD
    WHERE
        title LIKE CONCAT('%', #{searchWord}, '%') OR
        content LIKE CONCAT('%', #{searchWord}, '%')
</select>
  • query에서 페이징 처리를 하기 위해 다른 기능은 그대로 둔 채, where 절에 searchWord와 비교하여 searchWord가 제목이나 본문에 포함되어 있는 게시글 목록을 반환하였다.
  • 다른 기능은 기본 목록 반환과 비슷하다.
  • 검색어를 입력하지 않고 호출한 경우에는 전체 목록이 출력되므로, Service단에서 검색어가 없는 경우는 빈 리스트와 0개의 개수가 반환되도록 처리해주었다.

0331

댓글 입력, 삭제 API

댓글 기능을 구현하기 위해서는 기존 게시글 입력, 삭제 기능과 비슷하지만 고려해야할 사항이 더 있었다.

  1. 게시글 삭제 시, 게시글 안에 있는 댓글까지 DB에서 삭제
  2. 댓글 입력 및 삭제 시 Board DB에서 comment_cnt 컬럼 값 증가 및 감소

컨트롤러 - 서비스 - 매퍼

// Controller
/**
* 댓글 입력
* @param comment
* @param request
* @return
*/
@PostMapping("/new")
public BaseResponse insertComment(@RequestBody Comment comment, HttpServletRequest request) {
    // 글번호 필수 체크
    if (comment.getBoardNo() == 0) {
        throw new BaseException(BaseResponseCode.CODE_100, new String[]{"글번호"});
    }
    // 댓글내용 필수 체크
    if (isEmpty(comment.getContent())) {
        throw new BaseException(BaseResponseCode.CODE_100, new String[]{"댓글내용"});
    }

    // request에 저장한 id를 받아와서 Comment 객체에 값을 넣어준다.
    long memberNo = getMemberNo(request);
    comment.setMemberNo(memberNo);

    long commentNo = commentService.insertComment(comment);
    Map<String, Object> data = new HashMap();
    data.put("commentNo", commentNo);

    return new BaseResponse(data);
}

/**
* 댓글 삭제
* @param commentNo
* @param request
* @return
*/
@PostMapping("/{commentNo}/delete")
public BaseResponse deleteBoard(@PathVariable("commentNo") long commentNo, HttpServletRequest request) {

    // 현재 로그인한 회원 번호와 댓글을 작성한 회원 번호를 비교해서 다른 경우에 예외 발생
    Comment info = commentService.getComment(commentNo);
    long memberNo = getMemberNo(request);
    if (memberNo != info.getMemberNo()) {
        throw new BaseException(BaseResponseCode.CODE_103);
    }

    commentService.deleteComment(commentNo);

    return new BaseResponse();
}

// Service
@Override
public long insertComment(Comment comment) {
    // 댓글 추가 시 댓글 수 1 증가
    commentMapper.save(comment);
    boardMapper.updateCommentCnt(comment.getBoardNo(), 1);
    return comment.getCommentNo();
}

@Override
public Comment getComment(long commentNo) {
    return commentMapper.getComment(commentNo);
}

@Override
public void deleteComment(long commentNo) {
    // 댓글 번호로 게시글 번호 조회해서 댓글 삭제 시 댓글 수 1 감소
    long boardNo = commentMapper.getComment(commentNo).getBoardNo();
    commentMapper.deleteComment(commentNo);
    boardMapper.updateCommentCnt(boardNo, -1);
}

// Mapper
void save(Comment comment);

Comment getComment(long commentNo);

void deleteComment(long commentNo);

void deleteBoardComment(long boardNo);

mybatis query

<!-- 댓글 작성 -->
<insert id="save" parameterType="Comment">
    INSERT INTO COMMENT
    (
        BOARD_NO,
        MEMBER_NO,
        CONTENT,
        REG_DATE
    )
    VALUES
    (
        #{boardNo},
        #{memberNo},
        #{content},
        NOW()
    )
    <selectKey keyProperty="commentNo" resultType="long" order="AFTER">
        SELECT LAST_INSERT_ID()
    </selectKey>
</insert>

<!-- 댓글 조회 -->
<select id="getComment" parameterType="long" resultType="Comment">
    SELECT
        COMMENT_NO,
        BOARD_NO,
        MEMBER_NO,
        CONTENT,
        REG_DATE
    FROM COMMENT
    WHERE COMMENT_NO = #{commentNo}
</select>

<!-- 댓글 삭제 -->
<delete id="deleteComment" parameterType="long">
    DELETE FROM COMMENT
    WHERE COMMENT_NO = #{commentNo}
</delete>

<!-- 게시글에 있는 모든 댓글 삭제 -->
<delete id="deleteBoardComment" parameterType="long">
    DELETE FROM COMMENT
    WHERE BOARD_NO = #{boardNo}
</delete>
  • 게시글을 삭제할 경우 BoardServiceImpl에서 CommentMapper에 접근하여 deleteBoardComment를 통해 게시글에 있는 모든 댓글을 삭제하도록 구현하였다.
  • 댓글을 입력, 삭제할 경우 CommentServiceImpl에서 BoardMapper에 접근하여 댓글 수 컬럼을 변경하였다.

0401

댓글 목록 조회 API

댓글 목록 조회 API는 게시글 목록 조회 API와 비슷하여 쉽게 구현하였다.

컨트롤러 - 서비스 - 매퍼

// Controller
/**
* 게시글에 있는 댓글 목록 조회
* @param criteria
* @return
*/
@GetMapping("/lists")
public BaseResponse getBoardCommentList(@ModelAttribute Criteria criteria) {
    // 글번호 필수 체크
    if (criteria.getBoardNo() == 0) {
        throw new BaseException(BaseResponseCode.CODE_100, new String[]{"글번호"});
    }

    // 리턴 값의 순서를 보장하기 위해 LinkedHashMap으로 구현
    Map<String, Object> data = new LinkedHashMap<>();

    // boardNo로 게시글에 있는 댓글들을 Criteria로 페이징 처리하여 목록을 map 타입으로 반환
    Map<String, Object> result = commentService.getBoardCommentList(criteria);

    data.put("currentPageNo", criteria.getCurrentPageNo());
    data.put("recordsPerPage", criteria.getRecordsPerPage());
    data.put("totalCnt", result.get("totalCnt"));
    data.put("list", result.get("list"));
    return new BaseResponse(data);
}

// Service
@Override
public Map<String, Object> getBoardCommentList(Criteria criteria) {
    Map<String, Object> result = new HashMap<>();

    List<Comment> list = commentMapper.getBoardCommentList(criteria);
    int totalCnt = commentMapper.getBoardCommentTotalCnt(criteria.getBoardNo());

    result.put("list", list);
    result.put("totalCnt", totalCnt);

    return result;
}

// Mapper
List<Comment> getBoardCommentList(Criteria criteria);

int getBoardCommentTotalCnt(long boardNo);

mybatis query

<!-- 게시글에 있는 댓글 목록 조회 -->
<select id="getBoardCommentList" parameterType="Criteria" resultType="Comment">
    SELECT
        B.COMMENT_NO,
        B.BOARD_NO,
        A.MEMBER_NO,
        A.MEMBER_NAME,
        B.CONTENT,
        B.REG_DATE
    FROM MEMBER A INNER JOIN COMMENT B ON A.MEMBER_NO = B.MEMBER_NO
    WHERE BOARD_NO = #{boardNo}
    ORDER BY B.COMMENT_NO
    LIMIT #{recordsPerPage} OFFSET #{startPage}
</select>

<!-- 게시글에 있는 댓글 개수 반환 -->
<select id="getBoardCommentTotalCnt" parameterType="long" resultType="int">
    SELECT COUNT(*)
    FROM COMMENT
    WHERE BOARD_NO = #{boardNo}
</select>
  • 게시글에 있는 댓글 개수를 반환하므로 boardNo로 조회하여 목록을 반환하는 것을 제외하면 게시글 목록 조회와 거의 같은 방식으로 구현하였다.
  • 다만 parameterType으로 페이징 처리를 하기 위해 Criteria 클래스를 주었는데, boardNo를 포함시키기 위해 Criteria 클래스에 boardNo를 넣는 것이 논리적으로 합당한가에 대한 의문이 들었다. 이에 대해서는 좀 더 생각해 볼 필요가 있는 것 같다.

0402

좋아요 기능 API

좋아요 기능을 구현하기 위해서는 이전에 비해 고민해야할 부분이 조금 더 있었다. 가장 큰 고민거리는 좋아요를 클릭할 때마다 꺼지고 켜지고가 반환되어야 하고, 그 결과가 DB에 저장되어야 하는데 이를 어떻게 설계하느냐였다.

  • 좋아요를 누를 때마다 DB에서 생성하고 삭제하는 방식 대신, 컬럼을 추가하여 확인하도록 구현하였다.
  • 컬럼은 boolean 형식으로 할 지, 0이나 1의 숫자 형식으로 할 지 고민하다가 결국 ‘Y’, ‘N’으로 여부를 확인하도록 정했다.

컨트롤러 - 서비스 - 매퍼

// Domain
@Data
public class Like {
    private long likeNo;
    private long boardNo;
    private long memberNo;
    private String likeYn;
}

// Controller
/**
* 좋아요 클릭 시 동작
* @param like
* @param request
* @return
*/
@PostMapping("/update")
public BaseResponse clickLike(@RequestBody Like like, HttpServletRequest request) {
    // 글번호 필수 체크
    if (like.getBoardNo() == 0) {
        throw new BaseException(BaseResponseCode.CODE_100, new String[]{"글번호"});
    }

    // request에 저장한 id를 받아와서 Comment 객체에 값을 넣어준다.
    long memberNo = getMemberNo(request);
    like.setMemberNo(memberNo);

    Map<String, Object> data = likeService.clickLike(like);

    return new BaseResponse(data);
}

// Service
public Map<String, Object> clickLike(Like like) {
    Map<String, Object> result = new HashMap<>();

    // 회원 번호와 게시글 번호로 좋아요 DB를 조회하여 Like 객체 반환
    Like memberLike = likeMapper.getLike(like);

    // Like 객체가 없을 경우, Like 객체 추가한 뒤 반환
    if (ObjectUtils.isEmpty(memberLike)) {
        like.setLikeYn("Y");
        likeMapper.insertLike(like);
        result.put("likeYn", like.getLikeYn());
        return result;
    }

    // Like 객체가 있을 경우, likeYn 필드를 "Y"는 "N"으로 "N"은 "Y"로 하여 업데이트
    if (memberLike.getLikeYn().equals("Y")) {
        like.setLikeYn("N");
    } else {
        like.setLikeYn("Y");
    }

    // likeNo로 조회하기 위해, 가져온 Like 객체의 likeNo 값을 대입
    like.setLikeNo(memberLike.getLikeNo());
    likeMapper.updateLike(like);
    result.put("likeYn", like.getLikeYn());

    return result;
}

// Mapper
public interface LikeMapper {
    Like getLike(Like like);

    void insertLike(Like like);

    void updateLike(Like like);
}

mybatis query

<!-- 좋아요 조회 -->
<select id="getLike" parameterType="Like" resultType="Like">
    SELECT
        LIKE_NO,
        BOARD_NO,
        MEMBER_NO,
        LIKE_YN
    FROM LIKE_TABLE
    WHERE BOARD_NO = #{boardNo} AND MEMBER_NO = #{memberNo}
</select>

<!-- 좋아요 추가 -->
<insert id="insertLike" parameterType="Like">
    INSERT INTO LIKE_TABLE
    (
        BOARD_NO,
        MEMBER_NO,
        LIKE_YN
    )
    VALUES
    (
    #{boardNo},
    #{memberNo},
    #{likeYn}
    )
</insert>

<!-- 좋아요 업데이트 -->
<update id="updateLike" parameterType="Like">
    UPDATE LIKE_TABLE
    SET
        LIKE_YN = #{likeYn}
    WHERE LIKE_NO = #{likeNo}
</update>
  • 파라미터로 받은 회원 번호와 게시글 번호로 해당하는 객체가 있는지 DB를 조회하여 반환하였다.
  • 이 객체가 null이라면 DB에 새로 생성하는 쿼리를 실행하였고, 그렇지 않다면 likeYn 값만 바꾸어 update 쿼리를 실행하였다.
  • 업데이트 시, likeNo로 변경할 로우를 결정하기 위해서, memberLike에 저장해두었던 likeNo를 세팅하였다.

0403

좋아요 API - 추가 사항

작성한 좋아요 API에서 수정 및 추가해야 할 기능 몇 가지가 있었다.

  • 좋아요 변경 여부에 맞게 게시글 좋아요 수 동기화
  • 게시글 삭제 시 좋아요 데이터 정보도 삭제
  • 게시글 세부 조회 시 해당 회원의 게시글에 대한 좋아요 여부 반환

좋아요 변경 여부에 맞게 게시글 좋아요 수 동기화

게시글 목록에서 회원들이 좋아요를 클릭한 횟수를 보여주기 위해서 회원이 게시글에 좋아요를 클릭했을 때, boardMapper을 호출하여 좋아요 수를 변경하는 로직을 추가하였다.

// LikeServiceImpl
@Override
public Map<String, Object> clickLike(Like like) {
    Map<String, Object> result = new HashMap<>();

    // 회원 번호와 게시글 번호로 좋아요 DB를 조회하여 Like 객체 반환
    Like memberLike = likeMapper.getLike(like);

    // Like 객체가 없을 경우, Like 객체 추가한 뒤 반환
    if (ObjectUtils.isEmpty(memberLike)) {
        like.setLikeYn("Y");
        likeMapper.insertLike(like);
        boardMapper.updateLikeCnt(like.getBoardNo(), 1);
        result.put("likeYn", like.getLikeYn());
        return result;
    }

    // likeNo로 조회하기 위해, 가져온 Like 객체의 likeNo 값을 대입
    like.setLikeNo(memberLike.getLikeNo());

    // Like 객체가 있을 경우, likeYn 필드를 "Y"는 "N"으로 "N"은 "Y"로 하여 업데이트
    if (memberLike.getLikeYn().equals("Y")) {
        like.setLikeYn("N");
        likeMapper.updateLike(like);
        boardMapper.updateLikeCnt(like.getBoardNo(), -1);
    } else {
        like.setLikeYn("Y");
        likeMapper.updateLike(like);
        boardMapper.updateLikeCnt(like.getBoardNo(), 1);
    }

    result.put("likeYn", like.getLikeYn());

    return result;
}

// boardMapper
void updateLikeCnt(long boardNo, int amount);

mybatis query

<!-- 좋아요 여부 추가, 변경 시 likeCnt 변경 -->
<update id="updateLikeCnt">
    UPDATE BOARD
    SET LIKE_CNT = LIKE_CNT + #{amount}
    WHERE BOARD_NO = #{boardNo}
</update>
  • 게시글 번호와 증가량을 매개변수로 받는 mapper를 작성하여 like가 증가할 때는 1을 인자로 보내고, 감소할 때는 -1을 인자로 보내어 로직을 구현했다.

게시글 삭제 시 좋아요 데이터 정보도 삭제

// boardServiceImpl
@Override
public void deleteBoard(long boardNo) {
    boardMapper.deleteBoard(boardNo);
    commentMapper.deleteBoardComment(boardNo);
    likeMapper.deleteBoardLike(boardNo);
}

// likeMapper
void deleteBoardLike(long boardNo);

mybatis query

<!-- 게시글에 있는 모든 좋아요 삭제 -->
<delete id="deleteBoardLike" parameterType="long">
    DELETE FROM LIKE_TABLE
    WHERE BOARD_NO = #{boardNo}
</delete>
  • 게시글을 삭제할 때 likeMapper에 boardNo를 인자로 보내어 해당하는 게시글에 있는 좋아요 정보를 모두 삭제했다.

게시글 세부 조회 시 해당 회원의 게시글에 대한 좋아요 여부 반환

프론트 측에서 게시글 세부 조회를 하면 기존에 해당 게시글에 좋아요를 누른 회원은 좋아요 표시가 된 채로 게시글이 보여져야 하기 때문에 좋아요 여부를 함께 반환하도록 코드를 수정했다.

// Controller
/**
* 게시글 세부 조회
* @param boardNo
* @param request
* @return
*/
@GetMapping("/{boardNo}")
public BaseResponse getBoard(@PathVariable("boardNo") long boardNo, HttpServletRequest request) {

    long memberNo = getMemberNo(request);
    Board board = boardService.getBoard(boardNo);
    String likeYn = boardService.getBoardLikeYn(boardNo, memberNo);

    Map<String, Object> info = new LinkedHashMap<>();
    info.put("boardNo", board.getBoardNo());
    info.put("memberNo", board.getMemberNo());
    info.put("memberName", board.getMemberName());
    info.put("title", board.getTitle());
    info.put("content", board.getContent());
    info.put("commentCnt", board.getCommentCnt());
    info.put("likeCnt", board.getLikeCnt());
    info.put("likeYn", likeYn);
    info.put("regDate", board.getRegDate());

    Map<String, Object> data = new HashMap<>();
    data.put("info", info);

    return new BaseResponse(data);
}

// boardServiceImpl
@Override
public String getBoardLikeYn(long boardNo, long memberNo) {
    return likeMapper.getLikeYn(boardNo, memberNo);
}

// likeMapper
String getLikeYn(long boardNo, long memberNo);

mybatis query

<!-- 해당 게시글에 대한 회원의 좋아요 여부 반환 -->
<select id="getLikeYn" resultType="String">
    SELECT LIKE_YN
    FROM LIKE_TABLE
    WHERE BOARD_NO = #{boardNo} AND MEMBER_NO = #{memberNo}
</select>
  • likeYn을 추가해서 반환하기 위해 Board 객체로 받아온 것을 새로운 Map을 만들어서 넣었다.
  • 게시글 번호와 회원 번호를 통해 likeYn 여부를 조회하여 반환했다.

0404

SimpleBoard API 서버를 구현하며…

이번 프로젝트를 진행하면서 API 서버 구현이 마무리 되었다. 원하는 결과값을 돌려받는 것을 확인하였고, 처음부터 끝까지 내가 혼자 구현하며 많은 것을 배웠다.
작은 프로젝트에도 생각보다 고민할 거리가 정말 많다는 것을 깨달았고, 그것들을 구현하기 위해서는 많은 시간이 소요된다는 것을 몸소 체험했다.
의미있는 로그를 출력하고 쓸모 없는 dependency를 추가하지 않기 위해서도 노력을 기울이는 등 많이 부족하지만 최대한 실무라고 생각하고 정성을 다해서 프로젝트를 진행하였다.
아직 프론트엔드 부분은 제대로 시작하지도 않았고 갈 길이 멀지만, 두 서버를 연결하는 것을 시작으로 이번 프로젝트를 잘 마무리 지어서 다음 프로젝트로 나아갈 준비를 해야겠다.

태그:

카테고리:

업데이트: