Skip to content

suyoung049/Poki

Folders and files

NameName
Last commit message
Last commit date

Latest commit

Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

Poki

포킀(Poki) - λΆ€λͺ¨-μžλ…€ μ†Œν†΅μ„ λ†’μ΄λŠ” 온라인 ν”Œλž«νΌ

  • PokiλŠ” 맞벌이 κ°€μ •μ˜ λΆ€λͺ¨λ‹˜κ³Ό 아이듀을 μœ„ν•œ μƒν˜Έμž‘μš© μ„œλΉ„μŠ€μž…λ‹ˆλ‹€.
  • λΆ€λͺ¨λ‹˜μ€ 멀리 μžˆλŠ” μžλ…€μ™€λ„ OK! 아이듀과 λŠκ»΄λ³΄μ§€ λͺ»ν–ˆλ˜ μ†Œμ€‘ν•œ κ²½ν—˜μ„ μŒ“μ•„λ³΄μ„Έμš”.

πŸ“‹ ν”„λ‘œμ νŠΈ ν¬μŠ€ν„°

poster.pdf

🧐 How to use

  • 둜그인 ν›„ μžλ…€μ™€ λΆ€λͺ¨λ‹˜μ€ 고유 μ½”λ“œλ‘œ μ—°κ²°ν•˜κ²Œ λ©λ‹ˆλ‹€.
  • μžλ…€λŠ” κ°€μ§€κ³  싢은 선물을 κ²€μƒ‰ν•˜μ—¬ μœ„μ‹œλ¦¬μŠ€νŠΈμ— 등둝할 수 μžˆμŠ΅λ‹ˆλ‹€.
  • λΆ€λͺ¨λ‹˜μ€ μžλ…€κ°€ λ“±λ‘ν•œ μœ„μ‹œλ¦¬μŠ€νŠΈλ₯Ό 보고 μžλ…€μ—κ²Œ μ£Όκ³  싢은 선물을 μ„ νƒν•˜κ²Œ 되고 ν¬λ„μ•Œ λͺ¨μœΌκΈ°λ₯Ό μ‹œμž‘ν•˜κ²Œ λ©λ‹ˆλ‹€.
  • λΆ€λͺ¨λ‹˜μ€ μžλ…€μ—κ²Œ λ―Έμ…˜μ„ 쀄 수 μžˆμŠ΅λ‹ˆλ‹€.
  • μžλ…€λŠ” λ―Έμ…˜μ„ μˆ˜ν–‰ν•˜κ³  ν¬λ„μ•Œμ„ λ°›κ²Œ λ©λ‹ˆλ‹€.
  • 31개의 ν¬λ„μ•Œμ„ λͺ¨μœΌκ²Œ 되면 λΆ€λͺ¨λ‹˜μ€ μžλ…€μ—κ²Œ 선물을 주게 λ©λ‹ˆλ‹€.

πŸš€ μ£Όμš” κΈ°λŠ₯

image

πŸš€ ν¬λ„μ•Œ λͺ¨μœΌκΈ°

31개의 ν¬λ„μ•Œμ„ λͺ¨μ•„λ³΄μ„Έμš”.

ν¬λ„μ•ŒλΆ™νžˆκΈ°

πŸš€ μ„ λ¬Ό 검색

μ›ν•˜λŠ” 선물을 선택해 λ³΄μ„Έμš”.

선물검색

πŸš€ AI λ―Έμ…˜ μΆ”μ²œ

AIλ₯Ό ν™œμš©ν•˜μ—¬ λ―Έμ…˜μ„ μΆ”μ²œλ°›μ•„ λ³΄μ„Έμš”.

AIλ―Έμ…˜ μΆ”μ²œ

πŸš€ λ―Έμ…˜ μ˜ˆμ•½

λ―Έμ…˜μ„ μ˜ˆμ•½ν•΄ λ³΄μ„Έμš”.

λ―Έμ…˜μ˜ˆμ•½

πŸš€ μ±„νŒ…

μ±„νŒ…μœΌλ‘œ λΆ€λͺ¨λ‹˜κ³Ό μ†Œν†΅ν•΄λ³΄μ„Έμš”.

πŸš€ 화상 톡화

λΆ€λͺ¨λ‹˜κ³Ό 화상 톡화λ₯Ό ν•΄λ³΄μ„Έμš”.

화상톡화

πŸš€ μ›Ή-μ•±

λͺ¨λ°”μΌμ—μ„œ μ„€μΉ˜ν•˜μ—¬ μ›Ήκ³Ό μ•±μ—μ„œ λͺ¨λ‘ μ‚¬μš©ν•΄λ³΄μ„Έμš”.

πŸš€ μΊ˜λ¦°λ”

μΊ˜λ¦°λ”λ‘œ ν¬λ„μ•Œ 및 선물을 μ€€ 기둝을 확인해 λ³΄μ„Έμš”.

μΊ˜λ¦°λ”

πŸš€ μ•Œλ¦Ό

λ©”μ‹œμ§€, 화상톡화, λ―Έμ…˜ λ“±μ˜ μ•Œλ¦Όμ„ λ°›μ•„λ³΄μ„Έμš”.

μ•Œλ¦Ό

πŸ“š ν”„λ‘œμ νŠΈ μ§„ν–‰μ‹œ 맑은 λΆ€λΆ„

  • Board & Wishlist CRUD 섀계

    • μœ μ €κ°„ Board 동기화λ₯Ό μœ„ν•œ Client-Server 톡신 방식 κ°œμ„ 

      • μžλ…€μ˜ Whishlist 쀑에 선물을 선택, 포도 개수 31개 μ™„λ£Œν›„ μ„ λ¬Ό 증정 -> Board 생성, μ‚­μ œ ν•„μš”

      • λΆ€λͺ¨κ°€ μžλ…€μ—κ²Œ ν¬λ„μ•Œμ„ μ¦μ •ν•˜κ±°λ‚˜, μžλ…€κ°€ ν¬λ„μ•Œμ„ λΆ™νžκ²½μš° μ„œλ‘œμ˜ 화면에 정보 동기화 ν•„μš”

      • μ„œλΉ„μŠ€ λ©”μΈνŽ˜μ΄μ§€μ—μ„œλŠ” μœ„μ˜ λ‹€μˆ˜μ˜ μ΄λ²€νŠΈλ“€μ΄ λ°œμƒ, λ°œμƒ ν•  λ•Œλ§ˆλ‹€ λ³΄λ“œμ˜ 정보가 ν•„μš”

      • 초기 μ„€κ³„μ‹œ Poling λ°©μ‹μœΌλ‘œ 각각의 μ΄λ²€νŠΈκ°€ μ „λΆ€ Board 의 μƒνƒœλ₯Ό μš”μ²­ -> μ„œλ²„ κ³ΌλΆ€ν™” λ°œμƒ

      • 처음 메인 νŽ˜μ΄μ§€μ—μ„œ ν΄λΌμ΄μ–ΈνŠΈκ°€ μ„œλ²„μ— Board의 μƒνƒœλ₯Ό μš”μ²­ν›„ λŒ€κΈ°, 이벀트 λ°œμƒμ‹œ μ„œλ²„ μΈ‘μ—μ„œ μƒνƒœλ₯Ό μ‘λ‹΅ν•˜λŠ” SSE λ°©μ‹μœΌλ‘œ λ³€κ²½

    • μ†ŒμŠ€ μ½”λ“œ

      • λ©”μΈνŽ˜μ΄μ§€ μ ‘μ†μ‹œ ν΄λΌμ΄μ–ΈνŠΈ μΈ‘μ—μ„œ Board μƒνƒœ μš”μ²­ -> μ„œλ²„ μΈ‘ 처음 Board μƒνƒœ 응닡후 λŒ€κΈ°
      let globalVersion = 0; // Global version variable(μ „μ—­λ³€μˆ˜ μ§€μ •)
      @Sse('/grape/sse/user')
          async sseGetBoardByUserId(
            @GetUser() user: User,
            @GetUserId() id: number,
            @GetUserType() type: string,
          ): Promise<Observable<responseSseBoardDto>> {
        
            if (type !== 'PARENT') {
              id = await this.AuthService.getConnectedUser(user);
            }
            const use_grape = await this.boardService.getBoardByUserId(id);
            return new Observable<responseSseBoardDto>((observer) => {
              let localVersion = 0; // Local version variable
              const initialData = async () => {
                  // 맨 처음 λ³΄λ“œ μƒνƒœλ₯Ό 뢈러옴(Board μ‘΄μž¬ν•˜μ§€ μ•Šμ„ 경우)          
                  if (!use_grape) {
                      const initialResponse: responseSseBoardDto = {
                          data: {
                              code: 200,
                              success: true,
                              grape: {
                                  id:0,
                                  blank: 0,
                                  total_grapes: 0,
                                  attached_grapes: 0,
                                  deattached_grapes: 0,
                              },
                              is_existence: false,
                          },
                      };
                      observer.next(initialResponse);
                      localVersion = globalVersion;
                    // Update the local version
                      return;
                  }
                // 맨 처음 λ³΄λ“œ μƒνƒœλ₯Ό 뢈러옴(Board μ‘΄μž¬ν•  경우)
                  const initialResponse: responseSseBoardDto = {
                      data: {
                      code: 200,
                      success: true,
                      grape: await this.boardService.getBoardByUserId(id),
                      is_existence: true,
                      },
                  };
                  observer.next(initialResponse);
                  localVersion = globalVersion;
                  // Update the local version
                  };
      • μžλ…€κ°€ ν¬λ„μ•Œμ„ λΆ™νžˆκ±°λ‚˜ μ—¬λŸ¬ 이벀트 λ°œμƒμ‹œ μ„œλ²„μΈ‘μ—μ„œ Board의 μƒνƒœ 전솑
       //포도 λΆ€μ°© λ²„νŠΌ ν΄λ¦­μ‹œ μ‹€ν–‰ ν•¨μˆ˜
          @Post('/grape/attach')
          async attachBoard(
          ): Promise<responseBoardDto> {
              globalVersion += 1; // μ„œλ²„μ—μ„œ 이벀트 λ°œμƒ 확인
              return response
          }
      
      ---------------------------------------------------------------------------------------
           
          // λ‹€λ₯Έ μ΄λ²€νŠΈκ°€ λ°œμƒν–ˆμ„ 경우(λŒ€κΈ°μ€‘μΈ μ„œλ²„μ—μ„œ 이벀트 λ°œμƒμ‹œ Board μƒνƒœ 응닡)
                if (localVersion < globalVersion) {
                  const use_grape = await this.boardService.getBoardByUserId(id);
                  // 맨 처음 λ³΄λ“œ μƒνƒœλ₯Ό 뢈러옴(Wishlist μ¦μ •μœΌλ‘œ μΈν•œ Board μ‘΄μž¬ν•˜μ§€ μ•Šμ„ 경우)
                  if (!use_grape) {
                      const response: responseSseBoardDto = {
                          data: {
                              code: 200,
                              success: true,
                              grape: {
                                  id:0,
                                  blank: 0,
                                  total_grapes: 0,
                                  attached_grapes: 0,
                                  deattached_grapes: 0,
                              },
                              is_existence: false,
                          },
                      };
                      observer.next(response);
                      localVersion = globalVersion; // Update the local version
                      return;
                  }
                  // 맨 처음 λ³΄λ“œ μƒνƒœλ₯Ό 뢈러옴(Board μ‘΄μž¬ν•  경우)
                  const response: responseSseBoardDto = {
                    data: {
                      code: 200,
                      success: true,
                      grape: await this.boardService.getBoardByUserId(id),
                      is_existence: true,
                    },
                  };
                  observer.next(response);
                  localVersion = globalVersion; // Update the local version
                }
              };
              initialData(); 
              const intervalId = setInterval(updateData, 500);
              // ν΄λΌμ΄μ–ΈνŠΈ μΈ‘μ—μ„œ 연결이 λŠμ–΄μ‘Œμ„ 경우
              observer.complete = () => {
                clearInterval(intervalId);
              };
              
              localVersion = globalVersion;
              return observer;
            });
  • Chat WebSocket λ°±μ—”λ“œ λΆ€λΆ„ 섀계

    • μ±„νŒ… λ‚΄μ—­ μ €μž₯

      • μƒλŒ€λ°©κ³Όμ˜ μ±„νŒ… λ‚΄μ—­ μ €μž₯을 μœ„ν•΄ eventGateway μΈ‘μ—μ„œ Message 정보 직접 μ €μž₯

          @SubscribeMessage('message')
            async handleMessage(
                @ConnectedSocket() socket: Socket,
                @MessageBody() { roomName, message, user }: MessagePayload,
            ) {
                // Save message in database
                this.eventService.createMessage(user.user_id, message, roomName, user.id, user.name);
        
                socket.to(roomName).emit('message', { sender_id: user.user_id, message, check_id: user.id, createdAt: new Date(), sender_name: user.name });
        
                return { sender_id: user.user_id, message, check_id: user.id, createdAt: new Date(), sender_name: user.name };
            }
      • μ €μž₯된 μ±„νŒ… λ‚΄μ—­ μ±„νŒ… μž‘μ„±λœ μ‹œκ°„ 순으둜 리슀트 ν˜•μ‹μœΌλ‘œ ν΄λΌμ΄μ–ΈνŠΈ 츑에 응닡

            async getMessage(room_name: string): Promise<Message[]> {
                const messages = await this.messageRepository
                  .createQueryBuilder('message')
                  .where('message.conversation_id = :room_name', { room_name })
                  .orderBy('message.createdAt', 'ASC')
                  .getMany();
              
                return messages ;
              }
    • μ±„νŒ… μ•Œλ¦Όμ„ μœ„ν•œ μ±„νŒ…λ°© μž…μž₯/λΉ„μž…μž₯ ν‘œμ‹œ κ΅¬ν˜„

      • μ±„νŒ…λ°©μ— μƒλŒ€λ°© μ—†μ„μ‹œ λ©”μ„Έμ§€ λ‚΄μš© μ•Œλ¦ΌμœΌλ‘œ μ „μ†‘ν•˜λŠ” κΈ°λŠ₯ κ΅¬ν˜„

      • κ΅¬ν˜„ μœ„μ— μœ μ €κ°€ μ±„νŒ… νŽ˜μ΄μ§€μ— μž…μž₯μ‹œ μ§€μ •λœ DB에 μ €μž₯

      • μœ μ €κ°€ μ±„νŒ… νŽ˜μ΄μ§€μ— λ‚˜κ°ˆ μ‹œ DBμ—μ„œ μ‚­μ œ

        //μœ μ € μ±„νŒ…λ°©μ— μž…μž₯μ‹œ
         @SubscribeMessage('setUserName')
            async handleSetUserName(
                @MessageBody() data: { user_id: string },
                @ConnectedSocket() socket: Socket
            ) {
                await this.eventService.createChatSocketConnection(data.user_id, socket.id);
            }
        -----------------------------------------------------------------------------------------------
        // μœ μ € μ±„νŒ…λ°© 퇴μž₯μ‹œ
         @SubscribeMessage('disconnect')
            async handleDisconnect(@ConnectedSocket() socket: Socket) {
                try {
                    const disconnectedUser = await
                    this.eventService.findChatConnectionBySocketId(socket.id);
                    if (disconnectedUser) {
                        this.eventService.deleteChatConnection(disconnectedUser)
                    }
                } catch (error) {
                    this.logger.error(error);
                }
        
            }
            
      • DB에 μœ μ € 쑴재 여뢀에 λ”°λ₯Έ μž…μž₯/λΉ„μž…μž₯ ν‘œμ‹œ

        //@SubscribeMessage('message')
        const now_user = await this.authService.getUserById(user.id);
                // μ±„νŒ…λ°©μ— μƒλŒ€λ°©μ΄ μžˆλŠ”μ§€ 확인
                const connect_userId = await this.authService.getConnectedUser_id(now_user);
        
                const check = await this.eventService.checkChatConnection(connect_userId);
        
                if (!check) {
        
                    try {
        
                        const connect_id = await this.authService.getConnectedUser(now_user);
                        const pushToken = await this.pushService.getPushToeknByUserId(connect_id);
        
                        const title = 'μƒˆλ‘œμš΄ λ©”μ‹œμ§€κ°€ λ„μ°©ν–ˆμŠ΅λ‹ˆλ‹€.';
                        let info;
        
                        if (!message.startsWith('/static/media')) {
                            info = message;
                        }
                        else {
                            info = '이λͺ¨ν‹°μ½˜μ„ λ³΄λƒˆμŠ΅λ‹ˆλ‹€.'
                        }
                        await this.pushService.push_noti(pushToken, title, info);
                    } catch (exception) {
                        if (exception instanceof ForbiddenException) {
                            return { sender_id: user.user_id, message, check_id: user.id, createdAt: new Date(), sender_name: user.name };
                        }
        
                    }
                }

πŸ“š κΈ°μˆ μŠ€νƒ

λΆ„λ₯˜ 기술
Frontend
Backend
WebRTC
Database
Infrastructure/DevOps

🧩 μ„œλΉ„μŠ€ ꡬ쑰도

poki-service

πŸ‘¨β€πŸ‘¨β€πŸ‘¦β€πŸ‘¦ νŒ€μ›μ†Œκ°œ


BE νŒ€μž₯ : λ°•μ •μ˜

BE νŒ€μ› : 이수영

BE νŒ€μ› : μ „μœ μ§„

FE νŒ€μ› : κΉ€μž¬μ›

FE νŒ€μ› : μ§€μˆ˜ν˜„

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Contributors 5