화면 레이아웃

상세페이지 메인화면

  • HTML/CSS가 제공되는 것을 기본으로 작성해야 하며, 메인 페이지의 화면 구성은 아래 기획서대로 보여야 한다.

소스코드

JAVA - 이름규칙

  • JAVA Naming Conventions 을 지킨다.(참고 1) (참고 2)
  • 클래스의 이름과 메소드의 이름은 직관적으로 작성하도록 한다. 클래스의 이름과 메소드의 이름만 보아도,어떤 기능을 가지고 있을지 어떤 내용이 구현되어 있을지 짐작할 수 있어야 한다.
  • 코드를 읽는 사람이 개념을 쉽게 파악할 수 있도록 읽기 쉬운 코드를 작성하도록 한다. 예를 들어 변수 이름을 구체적으로 작성하도록 한다.

JAVA - 중복된 코드 제거 및 코드 구조화

  • 중복된 코드가 있다면, 별도의 메소드나 클래스로 분리하도록 한다.
  • 하나의 메소드가 너무 많은 코드를 담지 않는다. 코드의 양이 많을 경우 private한 메소드를 이용해서 메소드를 분리하거나 별도의 객체를 만들어 사용하도록 한다.
  • 클래스의 코드 길이가 너무 길어진다면, 해당 클래스가 몇개의 클래스로 분리될 수 있는지 고민한다.
  • 변수는 최대한 덜 사용하고, 최대한 가볍게 만들어 가독성을 높이도록 한다.

JAVA - 가독성

  • 조건문의 경우 긍정적이고 흥미로운 (주 흐름에 해당하는) 경우가 앞쪽에 위치하도록 한다.
  • 삼항연산자, do-while문장은 코드 가독성을 떨어트리니 되도록 사용하지 않는다.
  • 블럭이 너무 많이 중첩되면 코드를 읽기 어려워진다. 블록을 private메소드로 추출할 수 있는지 고민한다.
  • 코드는 빈줄을 이용해 커다란 블록을 논리적인 문단으로 구분한다.
  • 코드는 들여쓰기를 잘 지키도록 한다.
  • 필요하지 않은 코드는 제거한다.

JAVA - 프로젝트 구조

  • Controller, Service, Repository를 사용하여 구현되어 있어야 한다.
  • Controller에서 Service를 Service에서 Repository의 기능을 호출할 수 있지만, 그 반대는 허용되지 않는다.

Java - Web API

  • 상품 id를 전달 받아 해당 상품의 상품 정보, 예매자 리뷰 목록을 반환하는 Web API를 작성한다. 예매자 리뷰정보의 경우 전체를 제공한다.
  • SQL 문을 표준에 맞추어 작성하며 각 DBMS 별로 구현된 문법 사용은 최소화한다.
  • 데이터 수를 조회하는 경우 COUNT(*) 를 사용한다.

 

 

 

우선 평가 기준표를 꼼꼼하게 보았다. 그리고 Swagger 에 명시해놓은 각 항목에 대한 API 응답 값들을 확인 한 뒤, sql 쿼리문을 작성했다.

 

쿼리문들 이름은 "가져오려는 내용" + "BY 대표적으로 무엇을 통해서 가져오는지" 이런식으로 지어보았다.

 

Swagger 에 나와있는대로 Comment 부분 Response 와 Display_Info 부분의 Response 를 나누었다.

 

 

 

CommentDaoSql:
댓글을 보여주는 부분은 크게 둘로 나눠보았다.

public class CommentDaoSql {
  public static String SELECT_SOME_COMMENTS_BY_DISPLAY_INFO_ID =
  // Detail 페이지에서 보여줄 댓글들(3개만 불러오도록 설정) select
  ....
  public static String SELECT_ALL_COMMENTS_BY_DISPLAY_INFO_ID =
  // 한줄평 더보기를 눌렀을 때 나타나는 Review 페이지에서 보여줄 댓글들 select
  // (처음에 최대 10개까지 보여주고 댓글이 더 많을 경우 '더보기'버튼을 눌러서 ajax 로 10개씩 추가로 불러오도록 설정)
  ...
}

 

CommentImageDaoSql:

public static String SELECT_COMMENT_IMAGES_BY_RESERVATION_USER_COMMENT_ID = 
// 댓글 ID 값에 따른 댓글 이미지들에 대한 정보들 select
...

 

DisplayInfoDaoSql:

public class CommentImageDaoSql {
  public static String SELECT_DISPLAY_INFO_BY_DISPLAY_INFO_ID =        
  // display_info_id 값에 따른 display_info 에 해당하는 정보들 select 
  ...
}

 

DisplayInfoImageDaoSql:

public class DisplayInfoDaoSql {
  public static String SELECT_DISPLAY_INFO_IMAGE_BY_DISPLAY_INFO_ID =     
  // display_info_id 값에 따른 display_info_image (지도 이미지들)에 해당하는 이미지 정보들 select (
  ...
}

 

ProductImageDaoSql:

public class ProductImageDaoSql {
    public static String SELECT_PRODUCT_IMAGES_BY_DISPLAY_INFO_ID =     
    // display_info_id 값에 따른 product_image 들(슬라이딩 되는 상품 관련 이미지들)에 대한 정보들 select
    ...
}

 

ProductPriceDaoSql:

public class ProductPriceDaoSql {
    public static String SELECT_PRODUCT_PRICE_BY_DISPLAY_INFO_ID =    
    // display_info_id 값에 따른 product_price 들 (상품 가격들) 에 대한 정보들 select
    ...

이렇게 총 6개의 Sql 문들 추가하였다.

 

그 다음, 쿼리문들을 통해 데이터베이스에서 받아온 데이터들을 dto 에 RowMapper 클래스를 통해 맵핑 시켜준 값들을 반환하는 방법으로 dao 클래스들을 각자 작성했다. 3-1 에서 했던 방법과 동일하게 했다.

 

sql 문에 들어가는 변수 값들이 1개 일 때는 Collections 객체의 singletonMap 함수를 사용했고, 1개 보다 많을 때는 parameter 로 HashMap 을 사용하였다.

 

이전에 Service 항목에는 ProductService, CategoryService, PromotionService 이렇게 3개가 존재했다.

 

mainpage 페이지에서는 Product, Category, Promotion 이 3가지로 명확히 나눌수 있었는데, 상품을 누르고 detail 페이지에 들어가게 되면 여기서 처리되는 정보들은 모두 기존 Product 에 관련되어 있기 때문에 ProductService 에 모두 추가하는 방식으로 작성을 하였다.

 

리뷰 글을 작성하다보니 Service 를 어떻게 나눠야 하는지에 대한 의문이 생겼다.

 

MainpageService, DetailService, ReviewService 이런 식으로 나누는 것이 더 괜찮을 것 같은 생각이 드는데... 검색을 통해 Service 예제들을 더 찾아봐야겠다.

 

 

 

 

public interface ProductService {
    public static final Integer PRODUCT_LIMIT = 4;
    public static final Integer DETAIL_PAGE_COMMENTS_COUNT = 3;
    public static final Integer APPEND_COMMENTS_LIMIT = 10;
    public Map<String, Object> getProductResponseByCategoryId(Integer categoryId, Integer start);
    public Map<String, Object> getDisplayInfoResponseByDisplayInfoId(Integer displayInfoId); 
    public Map<String, Object> getCommentsByDisplayInfoId(Integer displayInfoId, Integer start); 
}

mainpage 에서 product 더보기를 누를 때 더해지는 갯수 제한을 4개로 할 때 상수 값을 Service 인터페이스 쪽에 작성했던 것과 같이 댓글에 대한 상수들도 여기에 작성해주었다.

 

Controller 부분도 Product 쪽만 추가했는데,

@RestController
@RequestMapping(path="/api/products")
public class ProductApiController {
    @Autowired
    ProductService productService;

    @GetMapping
    public ResponseEntity<Map<String, Object>> products(... , ...) {
        Map<String, Object> productResponse = ...
        ...
        return new ResponseEntity<Map<String, Object>>(productResponse, HttpStatus.OK);
    }

    @GetMapping("{displayInfoId}")
    public ResponseEntity<Map<String, Object>> displayInfo(...) {
        Map<String, Object> displayInfoResponse = ...
        ...
        return new ResponseEntity<Map<String, Object>>(displayInfoResponse, HttpStatus.OK);
    }

    @GetMapping("comments/{displayInfoId}")
    public ResponseEntity<Map<String, Object>> comments(... , ...) {
        Map<String, Object> commentsResponse = ...
        ...
        return new ResponseEntity<Map<String, Object>>(commentsResponse, HttpStatus.OK);
    }
}

(... 은 소스코드를 다 올리면 안되서 생략한 부분입니당)

위와 같이 API 설계를 하였고,

 

MainController 부분에

    @GetMapping(path="/review/{id}")
    public String review(@PathVariable(name = "id", required = true) Integer id) {
        return "review";
    }

한줄평 더보기를 눌렀을 때 댓글 보기 창으로 가도록 이 부분을 추가해주었다.

 

3-1 을 진행할 때는 Spring 을 알게 된지 얼마 되지 않아서 처음 부터 뚝딱뚝딱 하느라 시간도 많이 걸리고 막히는 부분도 많았는데 지금은 무언가를 해결하려고 할 때, 그리고 문제가 생겼을 때, 적당한 검색어를 통해 모르는 내용을 검색하여 최대한 빠른 시간 내에 해결할 수 있게 되어 프로젝트 진행 시간을 많이 줄일 수 있었다. 무엇보다 Spring 에 조금 익숙해진게 가장 큰 변화 같다. (뭔가 뿌듯..)

 

 

 

 

 

프로젝트를 진행하다보니 삘 받아서 FE 까지 후딱 끝내버리고 제출했더니 FE 을 진행하면서 기획서를 꼼꼼히 보지 않아 놓친 부분을 이렇게 알려주셨다...ㅠㅠㅜㅠㅜ 리뷰어님 천사...

 

 

우선 리뷰어님께서 리뷰해주신 부분들을 보자.

 

노트북이 살짝 버벅대는 탓에 조금 답답했던 이클립스 개발 환경에 지친 나는 vscode 에디터로 코드 작성을 병행하였는데... 여기서 문제점이 발생했다. 이클립스는 tab 이 4칸, vscode 에서는 2칸으로 설정 되어 있던 것이 아닌가 ! 크게 신경 쓰지 않고 지나간 나의 잘못이다. 앞으로는 꼭 들여쓰기는 똑같이 맞춰야겠다.

 

 

이런 부분은 생각치도 못했다. 추론을 통해 매개변수를 생략할 수 있는 점을 이용하면 코드가 좀 더 짧아지고 좀 더 깔끔해 질 것 같다.

 

 

CommentDao 에서 문제점을 지적해주셨다. 듣고 그 자리에서 일어나 무릎을 탁 쳤다. 사실 아직 dao, dto, vo, entity class 에 대해 완벽히 이해하지 못했기에 실수를 한 것 같다. 이 부분들에 대해서 좀 더 검색해보고 이해 해볼 것이다.. 지적해주신 부분은 확실하게 이해하였고 수정을 해야겠다.

 

 

 

selectDetailPageComments 부분과 selectReviewPageComments 을 나누다 보니 생긴 중복 되는 부분을 처리해주지 않았다. 이 두개를 Service 쪽으로 옮기고 해당 반복문 부분들을 private method 로 추출해봐야겠다.

 

 

Product Image 들을 불러 올 때, sql 문에 " AND pi.type != 'th' " 을 통해 썸네일 이미지 빼고 다 불러 오도록 했는데 쿼리에서 되도록 부정문을 사용하지 않는 것이 좋다고 하셨다. 데이터베이스 수업 때 교수님께서 이런 부분에 대해서 말씀하셨었는데... 이걸 그새 까먹었었다. 앞으로는 쿼리문에 최대한 부정문을 사용하지 않고 좀 더 명확한 조건을 선언해 주는 부분을 항상 생각하면서 코드를 짜야겠다.

 

 

 

 

3-1 에 이어 4-1 백엔드 부분 과제를 하면서 느낀 점이 정말 많다.

  1. Spring 은 정말 방대하고 공부할 것이 참 많은 것 같다. (하지만 재밌다)
  2. 나는 아직 정말 많이 부족하다. (더더더 열심히 해야겠다)
  3. 쉽게 짠 코드도 한번 더 생각하는 습관을 들여야 겠다.
  4. Spring 기본 MVC 에 대해 더 확실하게 이해해야 겠다. (책 하나 빌려야겠다)

 

 

 

 

 

#부스트 코스

 

+ 따끈한 최근 게시물