[발자국] 웹 프로젝트 (tripmark.co.kr)

[웹 프로젝트] 피드백 적용하기 5 - 프로젝트 목적에 맞는 디테일 설정

남건욱 2025. 7. 11. 22:28

목차

    반응형

    문제 정의

    피드백 5.
    
    여행인데 지도가 없네요.
    
    물론 사진으로 대체가 된다지만 개발자 입장에선 구글 맵 api라도 연동해서 지도를 달아두고..
    
    이동 경로 패스를 여행 패스와 싱크를 시켜주면 좋을 것 같아요.
    
    그리고.. 여행기록을 작성하였지만..
    
    
    
    0다녀온 나라 수
    0여행 횟수
    0여행 기간(일)
    최근 여행지
    아직 첫 여행을 떠나지 않았어요
    
    이라고 나오네요.
    
    
    여행 게임이라고 생각하고 어디에 갔는데 이 아이템을 획득했다. 그리고 이 아이템을 팔겠다.
    
    이런 프로세스라도 있으면 더 재미가 있지 않을까요?
    
    아니면 어떻게 하면 된다라는 예제라도 작성을 해두어야…

     

     

    해결 방향

    여행기록 게시판, 카드 로직 통합
    - 기존에 별도로 구현된 여행기록 카드 로직을 제거하고, 사용자가 게시판에 글을 작성하면
      해당 글의 데이터를 가져와 여행기록 카드에 노출되도록 변경하여 혼동을 해소함
    
    구글맵 API 연동으로 이동 경로 표시
    - Google Maps API를 사용해 사용자가 실제 이동한 경로(여행 패스)를 시각적으로 표시하고,
      여행기록 카드나 상세 페이지에서 해당 경로를 함께 보여주어 여행 궤적을 한눈에 파악할 수 있도록 구현함
      
    작성 가이드 추가
    - 카테고리마다 맞는 작성 가이드를 제공해야함

     

     

     

    구현 상세

    여행기록 게시판, 카드 로직 통합

    기존에는 위 사진들처럼 여행 기록 카테고리 게시글과 상관없이 따로 여행기록 카드를 추가, 수정, 삭제하고 조회 할 수 있도록 했다. 하지만 느낌이 겹쳐서 여행 기록 카테고리에 글을 작성하면 자동으로 설정되도록 구현되어있다고 말씀주신분이 많았다. 그래서 기존에 여행기록 카드를 따로 추가했던 부분을 제거하고 여행기록 카테고리 게시글을 작성하면 이에 맞게 연동하도록 수정해야겠다고 생각됐다.

     

    먼저 기존에 있던 카드 로직을 다 제거 해줬다. 그 뒤 기존의 게시글 관련 소스에 로직을 추가했다.

     

     

    // 반환 DTO
    
    @Getter
    @NoArgsConstructor
    public class TravelCardDto {
        private Long postId;
        private LocalDate startDate;       // 여행 시작일
        private LocalDate endDate;         // 여행 종료일
        private String countryName;        // 국가 이름
        private String countryFlagPath;    // 국기 이미지 경로
        private int travelPeriod;          // 여행 기간 (days)
        private String postTitle;          // 게시글 제목
        private LocalDateTime createdAt;   // 게시글 생성 시각
    
        public TravelCardDto(Post post) {
            this.postId = post.getId();
            this.startDate = post.getStartDate();
            this.endDate = post.getEndDate();
            this.countryName = post.getCountryName();
            this.countryFlagPath = post.getCountryFlagPath();
            this.travelPeriod = Period.between(post.getStartDate(), post.getEndDate()).getDays() + 1;
            this.postTitle = post.getTitle();
            this.createdAt = post.getCreatedAt();
        }
    }
    // controller
    
    /**
         * 특정 사용자가 작성한 여행기록 카테고리 게시글들을
         * 20개씩 페이지네이션하여 반환
         *
         * GET /api/posts/travel-cards/{nickname}?page=1
         *
         * @param nickname 조회할 사용자의 닉네임
         * @param page     페이지 번호 (1부터 시작, 기본값 1)
         * @return Page<TravelCardDto> (size=20 고정)
         */
        @GetMapping("/posts/travel-cards/{nickname}")
        public ResponseEntity<Page<TravelCardDto>> getTravelCards(
                @PathVariable String nickname,
                @RequestParam(name = "page", required = false, defaultValue = "1") int page
        ) {
            try {
                Page<TravelCardDto> travelCards = postService.getTravelCardsByNickname(nickname, page);
                return ResponseEntity.ok(travelCards);
            } catch (IllegalArgumentException e) {
                return ResponseEntity.status(HttpStatus.BAD_REQUEST).build();
            } catch (Exception e) {
                e.printStackTrace();
                return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).build();
            }
        }
    // service
    
     /**
         * 특정 사용자가 작성한 여행기록 카테고리 게시글들을
         * Page<TravelCardDto> 형태로 20개씩 페이징하여 반환
         *
         * @param nickname 조회할 사용자의 닉네임
         * @param page     요청 페이지 (1부터 시작)
         * @return Page<TravelCardDto> (size=20 고정, createdAt 내림차순)
         */
        @Transactional(readOnly = true)
        public Page<TravelCardDto> getTravelCardsByNickname(String nickname, int page) {
            // 1. 닉네임으로 User 조회
            User user = userRepository.findByNickname(nickname)
                    .orElseThrow(() -> new IllegalArgumentException("존재하지 않는 사용자입니다. nickname=" + nickname));
    
            // 2. Pageable 생성 (page-1: 0부터 시작, size=20, createdAt 내림차순)
            Pageable pageable = PageRequest.of(
                    Math.max(page - 1, 0),
                    20,
                    Sort.by(Sort.Direction.DESC, "createdAt")
            );
    
            // 3. PostRepository 에서 Page<Post> 조회
            Page<Post> postPage = postRepository.findByUserIdAndPostcategoryOrderByCreatedAtDesc(
                    user.getId(), PostCategory.TRAVEL_RECORD, pageable
            );
    
            // 4. Page<Post> 를 Page<TravelCardDto> 로 매핑하여 리턴
            return postPage.map(TravelCardDto::new);
        }
    // repository
    
    Page<Post> findByUserIdAndPostcategoryOrderByCreatedAtDesc(
                Long userId, PostCategory postcategory, Pageable pageable);

    이렇게 서버 로직을 추가 했다. 

     

    호출해본결과 원하던 결과가 나왔다.

     

    기존 여행카드 추가, 수정, 삭제를 제거한 뒤 조회만 남겨뒀다. 또한 무료이미지 100장을 넣어서 각 기록의 배경화면으로 사용하게 했다. 또한 여행기록을 클릭하면 해당 게시글로 이동하도록 설정해뒀다.

     

    구글맵 API 연동으로 이동 경로 표시

    = 이부분은 따로 제작중이다. (2025.07.11)

     

     

     

    작성 가이드 추가

    각 카테고리 게시글 작성페이지, 일기 작성 페이지에 플레이스홀더를 사용해서 간단한 작성가이드문구를 넣어뒀다. 또한 필터링 모델, 여행기록 카드에 툴팁을 추가해서 헷갈리는 사용자를 위한 설명을 추가했다.

     

     

     

     

     

     

    서버 주소

    https://tripmark.co.kr/

     

    발자국 - 여행 커뮤니티

    당신의 발자국으로 채워가는 여행플랫폼

    tripmark.co.kr

     

    ID: test1@gmail.com

    PW: !test1234

     

    테스트 계정 2

    ID: test2@gmail.com

    PW: !test1234

     

    피드백은 항상 감사히 받겠습니다.

    OKKY(개발자 지식공유 플랫폼)에서 피드백 받은 내용입니다.

    https://okky.kr/

     

    OKKY - All That Developer

    OKKY는 국내 최대 개발자 지식공유 플랫폼입니다. 개발자에게 필요한 기술 Q&A, 아티클, 커리어, 네트워킹, 취업, IT행사를 지원합니다

    okky.kr

     

    반응형
    프로필사진

    남건욱's 공부기록