UMC 1기 Server Session 5주차 워크북

redjen·2021년 11월 5일
0
post-thumbnail

목차

  1. 학습 목표
  2. 5주차 수업 후기
  3. 실습
  4. 핵심 키워드
  5. 논의해보면 좋은 것들

1. 학습 목표

  1. API에 대한 이해
  2. 벡엔드 랭귀지
  3. API test툴 활용 (Postman)
  4. Api sheet 작성

2. 5주차 수업 후기

💡 5주차 수업 듣고 느낀점 이야기, 각자 진행상황 공유 - 스프링을 통해 지난 주차에 설계해 놓았던 쿼리들을 API화하는 간단한 프로젝트를 진행했습니다. 처음부터 끝까지 인터넷을 뒤져가며 찾아보았고, 꽤 괜찮아 보이는 결과물이 나와서 처음엔 만족했습니다. 하지만 이 글을 쓰다 보니 부족한 부분이 계속 생각났고 추후에 리팩토링과 API 추가 작업을 통해 좀 더 좋은 프로젝트로 만들고 싶은 욕심이 생겼습니다.

3. 실습

📝실습 체크리스트

  • 개발환경 구축
  • 자신이 설계한 DB와 연동해서 API설계 (CRUD)
  • Postman으로 API테스트
  • API Sheet 작성

전체 코드 Github

프로젝트 구조

프로젝트 전체 구조는 아래와 같이

저에게 익숙한 Spring MVC 패턴으로 구성해보았습니다.

DB 접근 및 서비스 설계

MyBatis를 사용했습니다.
원래는 JPARepository를 사용하여 쿼리 질의를 하고 싶었으나 공부할 시간이 부족한 관계로 이전 실습에서 작성한 쿼리들을 바탕으로 그나마 사용경험이 있는 MyBatis로 환경을 구성하였습니다.
MyBatis Mapper XML 파일은 다음과 같이 작성하였습니다.

<ReviewService.java>

public interface ReviewService {

    int insertNewReview(int memberIdx, int companyIdx, int roomIdx, int reserveIdx, float rating, String reviewDescription);

    int insertReplyToReview(int reviewIdx, String reviewReply);

    List<Review> searchReviewList(int companyIdx);
}

<ReviewServiceImpl.java>

@Service
public class ReviewServiceImpl implements ReviewService{
    private ReviewMapper reviewMapper;

    @Autowired
    public ReviewServiceImpl(ReviewMapper reviewMapper) {
        this.reviewMapper = reviewMapper;
    }

    @Override
    public int insertNewReview(int memberIdx, int companyIdx, int roomIdx, int reserveIdx, float rating, String reviewDescription) {
        return reviewMapper.insertNewReview(memberIdx, companyIdx, roomIdx, reserveIdx, rating, reviewDescription);
    }

    @Override
    public int insertReplyToReview(int reviewIdx, String reviewReply) {
        return reviewMapper.insertReplyToReview(reviewIdx, reviewReply);
    }

    @Override
    public List<Review> searchReviewList(int companyIdx) {
        return reviewMapper.searchReviewList(companyIdx);
    }
}

<ApiController.java>의 /api/review/search에 대한 함수 발췌

@GetMapping("/review/search")
@ApiOperation(value="숙소 리뷰 목록 조회", notes="숙소의 모든 리뷰 목록을 조회한다.")
public ResponseEntity<Map<String, Object>> searchReviewList(@RequestParam int companyIdx) {
        Map<String, Object> resultMap = new HashMap<>();
        List<Review> reviewList = reviewService.searchReviewList(companyIdx);
        resultMap.put("data", reviewList);
        return new ResponseEntity<>(resultMap, HttpStatus.OK);
}

후기에 관련된 정보들은 전부 Review 테이블에 모여있습니다. Service와 Controller를 설계할 때 어떤 정보에 관한 서비스 / API 일까? 를 중점으로 생각하며 데이터 격리를 해보려 했습니다.

고쳐야 점은 전부터 @Autowired 어노테이션을 남발했다는 것입니다. 간단한 프로젝트이고, 주어진 시간도 많이 없다보니 이렇게 사용했지만, 실제로 사용할 프로젝트라면 @Autowired가 아닌 생성자를 통해 의존성을 주입받아야 한다고 배웠습니다. 후에 프로젝트를 좀 다듬는 다면 반드시 생성자를 통한 의존성 주입을 먼저 해야겠습니다. Autowired 어노테이션의 단점

API 명세서 : Swagger를 적용하였습니다.

com.redjen.yanolja.configuration의 <SpringFoxConfig.java>로 swagger를 설정해주고,
api controller의 각 controller에 @ApiOperation 어노테이션을 추가해주어 간단하게 API 명세서를 작성할 수 있었습니다.

@Configuration
public class SpringFoxConfig {

    @Bean
    public Docket api() {
        return new Docket(DocumentationType.SWAGGER_2)
                .select()
                .apis(RequestHandlerSelectors.any())
                .paths(PathSelectors.ant("/api/**"))
                .build();
    }
}

설정을 마치고 나서 localhost:8080/swagger-ui.html로 접속하면 다음 화면이 나옵니다.

local에서 swagger를 통해 다음과 같이 api 테스트도 가능했습니다.

🔥 트러블 슈팅(실패한 경험도 성장을 위한 경험!)

[ 문제 원인 ]
MyBatis XML Configuration 파일들을 찾지 못하여 오류 발생

[ 해결 방안 ]
application.properties에 mybatis.mapper-locations=mapper/**/*.xml를 추가해 주어 경로 설정해주었음.

[ 참고 자료 ]
https://codingdog.tistory.com/entry/mybatis-mapper-location-%EC%84%A4%EC%A0%95%EC%9D%84-%EC%89%BD%EA%B3%A0-%EB%B9%A0%EB%A5%B4%EA%B2%8C-%ED%95%B4-%EB%B4%85%EC%8B%9C%EB%8B%A4

4. 핵심 키워드

  • API : Application Programming Interface입니다. 응용 프로그램에서 사용할 수 있도록 운영체제나 프로그래밍 언어가 제공하는 기능을 제어할 수 있게 만든 인터페이스를 뜻합니다. 웹에서는 다른 서비스에 요청을 보내고 응답을 받기 위해 정의된 명세서를 말합니다. '도서관 API를 사용한다'라고 말하는 것은 도서관의 정보를 도서관 웹 사이트에 직접 접근하지 않고도 내가 원하는 도서의 정보를 얻고, 예약을 할 수 있는 것이 예시입니다.
  • Http 패킷 : 데이터를 통신망을 통해서 전송하기 쉽도록 자른 단위입니다. 패킷은 전의 포스팅에서도 다뤘었지만 우편물의 송장에 해당하는 정보들이 담겨 있어 신뢰성이 보장된 메시지를 효과적으로 보낼 수 있도록 도와주는 역할을 합니다.
  • Http 메소드
    • GET : MDN HTTP GET 문서를 참고했습니다. HTTP GET은 웹 페이지/서버에서 특정한 리소스를 가져오도록 요청하는 메서드입니다. GET 요청은 데이터를 가져올 때만 사용해야 하는 것이 특징입니다.
    • POST : MDN HTTP POST 문서를 참고했습니다. HTTP POST는 서버로 데이터를 전송하는 메서드입니다. 요청 본문의 유형은 Content-Type 헤더로 나타냅니다. 서버로 데이터를 전송하는 다른 메서드에는 PUT이 있습니다. PUT과 POST 요청의 차이점은 바로 PUT은 여러번 시도해도 같은 응답을 보장하는 반면, POST는 여러번 시도하였을 때 같은 응답을 받는다는 보장을 하지 못한다는 특징이 있습니다.
  • 데이터 포맷 : 각 엔티티들은 데이터를 주고 받는 과정에서 데이터를 목적에 맞게 다양한 방식으로 표현할 수 있습니다. 우리가 문서를 작성할 때 양식에 맞게 제출하면 읽는 사람 입장에서도 편하고, 쓰는 사람 입장에서도 데이터를 목적에 맞게 보낼 수 있는 것처럼 데이터를 각 포맷에 맞추어 송수신하는 것은 중요합니다. 웹에서 가장 많이 사용하는 데이터 포맷에는 JSON 포맷이 있습니다. JSON은 JavaScript Object Notation의 줄임말으로, 자바스크립트에서 객체 문법으로 구조화된 데이터를 표현하기 위한 표준 포맷입니다. 현대의 웹에서는 주로 JSON 포맷을 사용하여 데이터 송수신을 하고 있습니다.
  • API Sheet : 혼자서 복잡한 웹 페이지의 A to Z까지 개발하기란 매우 어려운 법입니다. 설령 성공적으로 가동되는 웹서버를 개발했더라도 이를 운영하고 유지 보수하는 일은 완전히 다른 영역이라고 생각합니다. 보통 프로젝트를 진행한다고 하면 기획서, 와이어 프레임, 기능 명세서와 같은 문서들을 작성하게 됩니다. API 명세서는 기본적인 API 스펙을 기술하는 문서입니다. 기획/설계 단계에서 기능 명세를 작성할 때 같이 작성해 놓으면 구조에 대해 고민할 시간이 생기고, 여러 사람이 동일한 기능에 대해 불필요한 커뮤니케이션 작업을 하지 않게 해주며, 무엇보다도 히스토리가 되어 기획 단계에서 이런 의도로 이런 기능 명세를 해 놨다는 것을 나중에 알게 되는 점이 실제로 API를 개발하는 사람에게는 길잡이가 되는 경우가 많습니다. 정리가 되지 않으면 아무리 리소스를 효율적으로 쓰는 코드라도 보기 힘들어지기 때문입니다. 다음 글에서 많은 인사이트를 얻었습니다.
  • path variable : 웹 API에서 데이터를 송수신하기 위해서는 어떤 엔드포인트에 Request를 보낼 것인지가 중요한 문제입니다. 엔드포인트는 크게 두 종류로 나뉘는데, Query String과 Path Variable이 그 두 가지입니다. https://medium.com/@fullsour/when-should-you-use-path-variable-and-query-parameter-a346790e8a6d에서는 Path Variable을 리소스를 식별할 때, Query Parameter는 리소스의 정렬이나 필터링을 할 때 사용해야 한다고 합니다. 예시를 들자면 제가 위에서 작성한 야놀자 프로젝트에서는 '멤버를 인덱스로 가져오는 API'를 작성해야 할 때 Query Parameter가 아닌 Path Variable을 사용하는 것이 적절하다고 합니다. 추후 리팩토링을 통해 바꿔야 할 코드가 늘어나게 되었습니다.

5. 논의해보면 좋은 것들

  • 각자 언어에 대해 논의
  1. Spring의 DI (Dependency Injection)을 어떻게 해야 좋은 설계일까?
  2. JPA, MyBatis, JDBC Repository의 특징과 장단점
  3. 자주 사용하게 되는 어노테이션
profile
make maketh install

0개의 댓글