inblog logo
|
[HootJem] 개발 기록 블로그
    springbootProject

    [방방곡곡] 프로젝트 회고

    HootJem's avatar
    HootJem
    Dec 04, 2024
    [방방곡곡] 프로젝트 회고
    Contents
    1. WF 그리기2. 기능 구현2-1. 여행지 리스트 구현2-2. 상세 페이지 구현2-3. 카카오 지도 API 사용3. 프로젝트 완성
    깃허브 주소 : https://github.com/hyeji111544/bangbangGokGok
     
    공공 데이터 포털 API 를 사용하여 국내 여행지, 음식점, 축제 등을 소개하는 프로젝트이다.
     
    팀원: 성훈님(팀장), 윤혜지(나), 은지님, 성윤님, 세연님 (총 5인)
    개발 기간 : 2024.09.04 ~ 2024.09.25
    사용 기술 : JAVA, SPRINGBOOT, HTML, JAVASCRIPT, JQUERY
     

    1. WF 그리기

    본인이 구현한 화면은 백엔드 기능까지 완성하기로 했기 때문에 피그마로 화면 작성부터 하였다.
    https://www.figma.com/design/VQj7FgVNewHxlEZsnFb9N2/Travel-Web-View
     
    notion image
    notion image
    notion image
    내가 담당하게 된 페이지는 이렇게 메인 페이지, 여행지, 상세 정보 페이지이다. (상세 정보 페이지는 내가 만든 페이지를 활용하여 공동으로 사용하게 되었다.)
    페이지 만들며 작성한 게시글 → https://inblog.ai/hj/html-구성을-위한-투쟁-28606
     

    2. 기능 구현

    2-1. 여행지 리스트 구현

    notion image
    제일 어려웠던 페이지는 이 페이지 였는데
    리스트를 출력하기 위해 최신순, 거리순, 인기순 , 시, 군, 구, 전체 이렇게 선택하는 항목과 그에 따라 처리해야 하는 데이터가 다 달랐기 때문이다.
    서비스에 시 클릭, SORTBY 클릭, 군 클릭, 이런식으로 나누어 만들지 않고,
    조회를 위해 필요한 매개변수들을 DTO 로 담아 내부에서 동적 쿼리를 사용하여 하나의 서비스로 해결하였다.
    해당 포스팅
    → https://inblog.ai/hj/36963 (리스트 출력)
    → https://inblog.ai/hj/36965 (여러 매개변수 추가 하여 작동하도록)
     
     

    2-2. 상세 페이지 구현

    notion image
    상세 페이지는 공공데이터로 API 요청을 보내서 출력되게 구성되어 있다.
    리턴 형식이 JsonData 라서 DTO 로 파싱하여야 했다.
    정말 희안하게 단일 데이터 임에도 body → items → item 내부에 배열로 존재했다.
     
    해당 포스팅
    → https://inblog.ai/hj/방방곡곡-jsondata-를-dto-로-파싱하기-37002
     
     
     

    2-3. 카카오 지도 API 사용

    https://getinthere.notion.site/14-API-6bfbf604b7124398a2796910a3dfd6fd
    고난에 빠진 팀원분을 도와주며 공부한 덕분에 내 기능 구현에도 유용하게 사용할 수 있었다. 👍
     

    3. 프로젝트 완성

    https://youtu.be/3B2B9fFRCs8?si=Ghba2lgVU5Dzx3AH
     
    이렇게 프로젝트는 잘 마무리 되었다.
     
    아쉬운 점은 댓글에 사진 첨부 기능이 없다는 점인데 DB 와 여러가지 추가할 사항이 많아져서 시간상 구현하지 못하고 끝나게 되었다.
    notion image
     

    배운점!

    공공API 를 사용할 때 내가 기대한 것 처럼 데이터가 오지 않았다. 이럴때 데이터의 구조를 확인하고 어떻게 꺼내서 프로젝트에 맞게 활용할 수 있는지 알 수 있었다.
     
    여러가지 파라미터가 추가된 검색을 이전에 쇼핑몰 프로젝트를 하며 구현했을때는 이렇게 QueryDSL 을 사용하여 구현했었다. 이번에는 JPQL 을 사용하여 구현할 수 있어서 여러 방법을 경험해서 좋았다.
    동적 쿼리를 사용하거나, 타입 안정성을 위해서는 QueryDSL 을 사용하는게 낫다고 하지만 기본적인 쿼리문 공부를 위해서 JPQL을 사용하는 의미가 있었다.
    https://github.com/hyeji111544/lotte-on/blob/main/src/main/java/kr/co/lotteon/repository/impl/ProductRepositoryImpl.java
    @Override public Page<Tuple> selectProductsByCate(ProductPageRequestDTO pageRequestDTO, Pageable pageable){ String sort = pageRequestDTO.getSort(); String seletedCate = pageRequestDTO.getCateCode(); OrderSpecifier<?> orderSpecifier = null; log.info("here1 : " + sort); if (sort != null && sort.startsWith("prodSold")){ orderSpecifier = qProduct.prodSold.desc(); }else if (sort != null && sort.startsWith("prodLowPrice")) { orderSpecifier = qProduct.prodPrice.asc(); }else if (sort != null && sort.startsWith("prodHighPrice")) { orderSpecifier = qProduct.prodPrice.desc(); }else if (sort != null && sort.startsWith("prodScore")) { orderSpecifier = qProduct.tReviewScore.desc(); }else if (sort != null && sort.startsWith("prodReview")) { orderSpecifier = qProduct.tReviewCount.desc(); }else if (sort != null && sort.startsWith("prodRdate")) { orderSpecifier = qProduct.prodRdate.desc(); }else if (sort != null && sort.startsWith("prodHit")) { orderSpecifier = qProduct.prodHit.desc(); }else if (sort != null && sort.startsWith("prodDiscount")){ orderSpecifier = qProduct.prodDiscount.desc(); }else { orderSpecifier = qProduct.prodSold.desc(); } QueryResults<Tuple> results = jpaQueryFactory .select(qProduct, qProductimg) .from(qProduct) .join(qProductimg) .on(qProduct.prodNo.eq(qProductimg.prodNo)) .where(qProduct.cateCode.like(seletedCate+"%")) .offset(pageable.getOffset()) .limit(pageable.getPageSize()) .orderBy(orderSpecifier) .fetchResults(); List<Tuple> content = results.getResults(); log.info(content.toString()); long total = results.getTotal(); log.info("total : {}", total); return new PageImpl<>(content, pageable, total); }
    https://github.com/hyeji111544/bangbangGokGok/blob/dev/src/main/java/green/mtcoding/travel/content/ContentRepository.java
    public List<Content> findByContentTypeId(String contentTypeId,String sortBy, Pageable pageable) { StringBuilder queryStr = new StringBuilder("select c from Content c where c.contentTypeId = :contentTypeId"); if (sortBy != null && !sortBy.isEmpty()) { if (sortBy.equals("createdTime")) { queryStr.append(" order by c.createdTime desc"); } else if (sortBy.equals("viewCount")) { queryStr.append(" order by c.viewCount desc"); } } Query query = em.createQuery(queryStr.toString(), Content.class); query.setParameter("contentTypeId", contentTypeId); query.setFirstResult((int) pageable.getOffset()); // 시작 위치 query.setMaxResults(pageable.getPageSize()); // 한 페이지에 표시할 최대 개수 return query.getResultList(); } public List<Content> findByContentTypeIdAndOption(String contentTypeId, String area, String sigunguCode,String sortBy, Pageable pageable) { StringBuilder queryStr = new StringBuilder("select c from Content c where c.contentTypeId = :contentTypeId and c.areaCode= :area"); if (sigunguCode != null && !sigunguCode.isEmpty()) { queryStr.append(" and c.sigunguCode = :sigunguCode"); } if (sortBy != null && !sortBy.isEmpty()) { if (sortBy.equals("createdTime")) { queryStr.append(" order by c.createdTime desc"); } else if (sortBy.equals("viewCount")) { queryStr.append(" order by c.viewCount desc"); } } Query query = em.createQuery(queryStr.toString(), Content.class); query.setParameter("contentTypeId", contentTypeId); query.setParameter("area", area); if (sigunguCode != null && !sigunguCode.isEmpty()) { query.setParameter("sigunguCode", sigunguCode); } query.setFirstResult((int) pageable.getOffset()); // 시작 위치 query.setMaxResults(pageable.getPageSize()); // 한 페이지에 표시할 최대 개수 return query.getResultList(); }
     
    Share article
    Contents
    1. WF 그리기2. 기능 구현2-1. 여행지 리스트 구현2-2. 상세 페이지 구현2-3. 카카오 지도 API 사용3. 프로젝트 완성

    [HootJem] 개발 기록 블로그

    RSS·Powered by Inblog