경매 입찰하기(실시간 가격변동)

SaGo_MunGcci·2022년 10월 8일
0

스프링

목록 보기
30/31
post-thumbnail

Trouble Shooting

  • 경매 입찰하기(실시간 가격변동)
    • 문제
      1. 상세화면에서 경매 입찰 가격이 실시간으로 변하지 않는 문제
      2. 상세화면에서 경매 가격 입력해 입찰 시 제대로 반영되지 않는 문제(정확한 낙찰자 선정이 안됨)
    • 원인파악
      • 1번 문제에 관한 원인파악
        a. 실시간 호가 입력 후 상세페이지에 접속해 있는 사용자들에게 전부 실시간으로 변경된 가격을 보여주어야 함.
        b. 가격 변동후 상세페이지에 입장하는 사람들 및 메인페이지에 접근하는 사용자에게도 변경된 가격을 보여주어야 함.
        • 따라서 a의 경우에는 소켓을 사용하여 실시간으로 데이터를 주고 받아야 했고
        • b의 경우에는 해당 경매 게시글의 경매가를 실시간으로 사용자들이 제시한 가격을 저장하여 보여 주어야 한다.
      • 2번 문제에 관한 원인 파악
        1. 경매 입찰 시 웹소켓 연결을 ENTER, TALK을 동시에 한다면 bid-message table에 동시에 2개의 데이터가 저장되는 문제였음

          —>서버 코드의 오류

          b. 입장시 바로 출력되는 ENTER인 메시지인 경우 클라이언트측에서 세션값에 이전 경매게시글에 방문했었던 호가 가격이 ENTER 메시지로 출력되는 오류

          —> 서버 코드의 오류

    • 선택지
      • 1번문제에 관한 선택지
        • 실시간 통신을 하기 위한 기술로 Polling, Long Polling, HTTP Streaming, WebSocket 중 WebSocket을 사용하기로 함. http를 이용한 단방향 통신보다는 양방향 통신이 가능한 WebSocket을 사용하는 것이 서버의 부담을 줄이고 실시간으로 전송하는 가격의 데이터를 테이블에 저장할수 있다고 판단함.
        • WebSocket 중 현재 사용중인 framework인 Spring에서 제공하는 Spring WebSocket및 채팅을 pub/sub으로 제어할 수 있는 Stomp를 선택
        • 실시간 통신중 끊어질 수 있는 경우를 대비하여 SockJS를 선택
        • 계속적인 테이블이 조회에 따른 부담을 최소화 할 수 있는 캐시시스템의 필요성및 다른 서버와도 pub/sub으로 통신할 수 있는 redis를 적용함.
      • 2번문제에 관한 선택지 코드오류로 파악되어 없음.
    • 의사결정
      1. 1번문제에 관한 의사결정
        1. 1:1채팅 및 전체 채팅과 별도로 해당 경매 가격만을 실시간으로 주고 받을수 있는 매핑주소가 필요함
        2. 상세화면으로 들어가자마자 웹소켓을 연결해 해당 유저를 입장시킴
        3. 입장한 유저가 호가를 입력
        4. 입력한 호가가 경매 테이블의 호가 컬럼에 반영되고 저장됨
        5. 입력된 호가를 레디스에도 저장
        6. 채팅 메시지를 RedisTemplate를 이용한 convertAndSend 해당 경매 게시글의 호가를 출력함.
        - —>입찰 가격을 올릴 때마다 웹소켓에 해당 유저가 입장한다고 설정 요약 하면 해당 경매 게시글의 호가 채팅방을 만듦

        클라이언트


methods: {
    findRoom: function () {
        axios.get('/chat/room/' + this.roomId).then(response => {
            this.room = response.data;
        });
    },
    sendMessage: function () {
			// 호가 전용 메세지 매핑 주소
       ws.send("/app/chat/bid", {}, JSON.stringify({
            type: 'TALK',
            roomId: this.roomId,
            sender: this.sender,
            message: this.message
        }));
        this.message = '';
    },
    recvMessage: function (recv) {
        this.messages.unshift({
            "type": recv.type,
            "sender": recv.type == 'ENTER' ? '[알림]' : recv.sender,
            "message": recv.message

        })
    }
}

서버

// 호가 주소
@MessageMapping("/chat/bid")
public void enterBid(BidMessageDto message) {
    chatService.saveBid(message);
}
@Transactional
public void saveBid(BidMessageDto message) {

    if (BidMessage.MessageType.ENTER.equals(message.getType())) {
        chatRoomService.enterChatRoom(message.getRoomId());
		}

    Member member = memberRepository.findByNickName(message.getSender()).orElseThrow(
            () -> new IllegalArgumentException("해당 닉네임 없음")
    );

    String nickName = member.getNickName();       

    LocalDateTime now = LocalDateTime.now();
    String createdAtString = now.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss", Locale.KOREA));

    BidMessage bidMessage = new BidMessage(message, nickName, createdAtString);

    //db에 저장
    bidMessageRepository.save(bidMessage);

    //redis에 저장
    BidMessage bidMessageRedis = chatMessageService.saveBid(bidMessage);

    System.out.println("==================redis 시간 :============" + bidMessageRedis.getCreatedAt());

    Long nowPrice = Long.parseLong(bidMessage.getMessage());

    Auction auction = auctionRepository.findByBidRoomId(bidMessage.getRoomId());

    auction.updateJoinPrice(nowPrice);        
    
    Participant participant = new Participant(member, auction);

    participantRepository.save(participant);

    Long participantCnt = participantRepository.countAllByAuctionId(auction.getId());

    auction.updateParticipantCnt(participantCnt);

    auctionRepository.save(auction);

    JoinPrice joinPrice = new JoinPrice(member.getId(), auction.getId(), nowPrice);

    joinPriceRepository.save(joinPrice);

    redisPublisher.publishBid(ChatRoomService.getTopic(bidMessage.getRoomId()), bidMessage);
}
  1. 2번문제에 관한 의사결정

    1. 1번에서 적용한 saveBid의 코드중 4번째 줄에 있는 if문에 else를 추가하지 않아서 위의 a,b번 문제를 제대로 처리해주지 못하고 있었음
    2. 코드 수정
    @Transactional
    public void saveBid(BidMessageDto message) {
    		// ENTER메시지인 경우
        if (BidMessage.MessageType.ENTER.equals(message.getType())) {
            chatRoomService.enterChatRoom(message.getRoomId());
    
        } else { // TALK메시지인 경우 위에서 else문을 추가하지 않아서 ENTER에 가격이
    						 // 본문에 타고 내려와서 저장되었음           
    
            Member member = memberRepository.findByNickName(message.getSender()).orElseThrow(
                    () -> new IllegalArgumentException("해당 닉네임 없음")
            );
    
            String nickName = member.getNickName();
    
            System.out.println("닉네임" + nickName);
    
            LocalDateTime now = LocalDateTime.now();
            String createdAtString = now.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss", Locale.KOREA));
    
            BidMessage bidMessage = new BidMessage(message, nickName, createdAtString);
    
            //db에 저장
            bidMessageRepository.save(bidMessage);
    
            //redis에 저장
            BidMessage bidMessageRedis = chatMessageService.saveBid(bidMessage);
    
            System.out.println("==================redis 시간 :============" + bidMessageRedis.getCreatedAt());
    
            Long nowPrice = Long.parseLong(bidMessage.getMessage());
    
            Auction auction = auctionRepository.findByBidRoomId(bidMessage.getRoomId());
    
            //Auction auction = auctionRepository.findByMember(member);
    
            auction.updateJoinPrice(nowPrice);
    				
    				// 해당 경매 게시글에 호가를 입력할때마다 내가 참여한 경매의 숫자가 늘어났던 오류를 해결한 코드.	
            if (participantRepository.existsByMemberIdAndAuctionId(member.getId(), auction.getId())) {
    
                auctionRepository.save(auction);
    
                JoinPrice joinPrice = new JoinPrice(member.getId(), auction.getId(), nowPrice);
    
                joinPriceRepository.save(joinPrice);
    
                redisPublisher.publishBid(ChatRoomService.getTopic(bidMessage.getRoomId()), bidMessage);
    
            } else {
    
                Participant participant = new Participant(member, auction);
    
                participantRepository.save(participant);
    
                Long participantCnt = participantRepository.countAllByAuctionId(auction.getId());
    
                auction.updateParticipantCnt(participantCnt);
    
                auctionRepository.save(auction);
    
                JoinPrice joinPrice = new JoinPrice(member.getId(), auction.getId(), nowPrice);
    
                joinPriceRepository.save(joinPrice);
    
                System.out.println("===============================================");
                System.out.println(bidMessage.getType());
                System.out.println(bidMessage.getRoomId());
                System.out.println(bidMessage.getSender());
                System.out.println(bidMessage.getMessage());
                System.out.println(bidMessage.getCreatedAt());
                System.out.println(bidMessage.getNickName());
                System.out.println("===============================================");
    
                //sendingOperations.convertAndSend("/topic/chat/room/" + bidMessage.getRoomId(), bidMessage);
    
                System.out.println("================================bidMessage roomid :" + bidMessage.getRoomId());
    
                redisPublisher.publishBid(ChatRoomService.getTopic(bidMessage.getRoomId()), bidMessage);
    
            }
        }
    }
  • 결과
    • 상세화면에 들어가자마자 웹소켓을 연결하여 해당 유저를 입장시켜 입찰 가격을 입력하지 않아도 실시간 가격 변동을 확인할 수 있음.



profile
이리저리 생각만 많은 사고뭉치입니다.

0개의 댓글