TIL

80일차 TIL

ohs020105 2025. 3. 7. 20:36

오늘 진행한 업무: 핑크토피아 업적 시스템 개

오늘은 핑크토피아 프로젝트의 업적 시스템을 개선하는 작업을 진행했다. 주로 완료된 업적과 진행 중인 업적을 정확하게 구분하고, 서브업적의 완료 상태를 포함한 데이터를 제공하는 API를 구현했다.

주요 개선 사항

1. 서브업적 완료 상태를 포함한 새로운 API 엔드포인트 추가했다

2. 프론트엔드에서 서브업적 완료 여부 로직 개선했다

 

3. 토큰 인증 방식 통일했다

 

🔍 문제 상황

1. 업적 조회 기능 불일치

브업적 데이터를 가져올 때 완료 상태 정보가 포함되지 않아 사용자별 진행 상황을 알 수 없었다. 특히 "진행중인 업적" 탭에서 실제로 진행 중인 업적만 표시되어야 하는데, 이 구분이 제대로 되지 않았다.

// 기존 코드 - 완료 상태 정보 없이 서브업적만 반환
async findOne(id: string): Promise<{ title: string; subAchievements: SubAchievement[] }> {
  // ...
  return {
    title: achievement.title,
    subAchievements: subAchievement ?? [],
  };
}

 

2. 인증 토큰 처리 불일치

프론트엔드의 다른 페이지들(예: 채팅룸)과 업적 페이지가 서로 다른 방식으로 인증 토큰을 처리하고 있어 일관성 문제가 발생했다.

 

// 기존 코드 - Bearer 접두사를 붙인 방식
const headers = accessToken ? { "Authorization": `Bearer ${accessToken}` } : {};

// 다른 페이지들의 코드 - 토큰만 전달하는 방식
const headers = { "Authorization": localStorage.getItem("accessToken") };

 

 

3. 서브업적 완료 상태 확인 로직 복잡

데이터의 구조가 일관되지 않아 서브업적의 완료 여부를 확인하는 로직이 매우 복잡했다.

 

 

💡 해결 방법

1. 사용자별 서브업적 완료 상태를 제공하는 새로운 API 엔드포인트 구현했다

 

// achievement.controller.ts에 새 엔드포인트 추가
@UseGuards(UserGuard)
@Get('/achievementId/:achievementId/withStatus')
async findOneWithStatus(
  @Param('achievementId') achievementId: string,
  @Request() req
) {
  const userId = req.user.id;
  console.log('상세조회 (완료 상태 포함)', achievementId, '사용자:', userId);
  return await this.achievementService.getAchievementWithSubAchievements(achievementId, userId);
}
// achievement.service.ts에 새 메서드 추가했다
async getAchievementWithSubAchievements(
  id: string,
  userId: number,
): Promise<{ title: string; subAchievements: any[] }> {
  // 업적 정보 가져오기
  // ...
  
  // 사용자가 완료한 서브업적 정보 가져오기
  const completedSubAchievements = await this.repository.findCompletedSubAchievements(userId, idA);
  
  // 완료 상태 정보 포함하여 반환
  // ...
}
// achievement.repository.ts에 완료된 서브업적 조회 메서드 추가했다
async findCompletedSubAchievements(userId: number, achievementId: number): Promise<AchievementP[]> {
  return await this.achievementPEntity.find({
    where: {
      user: { id: userId },
      achievement: { id: achievementId },
      completed: true
    }
  });
}

 

2. 프론트엔드 코드 수정으로 새 API 활용했다.

 

// 새로운 엔드포인트 사용 (완료 상태 포함)
let response = await fetch(`/achievement/achievementId/${achievement.id}/withStatus`, { headers });
                
// 새 엔드포인트가 구현되지 않았거나 실패하면 기존 엔드포인트 사용했다
if (!response.ok) {
  console.log(`새 엔드포인트 실패, 기존 엔드포인트 시도...`);
  // 기존 엔드포인트 시도
  response = await fetch(`/achievement/achievementId/${achievement.id}`, { headers });
  
  // 만약 404라면 다른 엔드포인트 시도했다
  if (!response.ok && response.status === 404) {
    console.log(`기본 엔드포인트에서 서브업적 못찾음, 두 번째 엔드포인트 시도...`);
    // 두 번째 엔드포인트 시도 (/achievement/:id)
    response = await fetch(`/achievement/${achievement.id}`, { headers });
  }
}

 

3. 토큰 처리 방식 통일했다

// 일관된 토큰 처리 방식으로 수정했다
const headers = accessToken ? { "Authorization": accessToken } : {};

 

🧪 테스트 및 결과

테스트 결과, 다음과 같은 효과를 얻을 수 있었다:

 

1. "진행중인 업적" 탭에서 최소 1개 이상의 서브업적이 완료된 업적만 정확하게 표시되었다

 

2. 서브업적 완료 상태가 시각적으로 정확히 표현되었다 (체크 표시)

 

3. 여러 페이지 간 일관된 인증 방식 적용되었다

 

🤔 어려웠던 

1. 데이터 구조의 일관성 부재: 서브업적 데이터의 구조가 여러 형태로 존재하여, 완료 상태를 확인하는 로직이 매우 복잡해졌다.

 

2. API 응답 처리의 복잡성: 여러 API 엔드포인트가 서로 다른 구조로 응답을 반환하여, 이를 일관되게 처리하는 것이 까다로웠다.

 

3. 인증 방식의 차이: 기존에 다른 페이지들과 업적 페이지 간의 인증 토큰 처리 방식이 달라서 통일하는 과정이 필요했다.

📚 배운 점

 

1. API 설계의 일관성 중요성: API 응답 구조를 일관되게 설계하면 프론트엔드 개발이 훨씬 쉬워진다.

 

2. 단계적 폴백(fallback) 전략: 새로운 API가 배포되기 전에도 기존 코드가 작동할 수 있도록 폴백 메커니즘을 구현하는 것이 중요하다.

 

3. 사용자 경험 중심 설계: 업적 시스템은 사용자 참여를 높이는 중요한 요소이므로, 진행 상황을 명확하게 보여주는 것이 중요하다.

 

추가로 업적 시스템에 추가로 알림 기능을 구현하여, 사용자가 새로운 업적을 완료했을 때 실시간으로 알림을 받을 수 있도록 개선할 예정이다.

'TIL' 카테고리의 다른 글

ec2 서버 배포 (nest.js)  (0) 2025.04.17
위치 기반 흐름 정리  (0) 2025.04.13
79일차 TIL (토스 구현 3일차)  (0) 2025.03.06
78일차 TIL (토스 결제 구현 2일차)  (1) 2025.03.04
77일차 TIL  (0) 2025.03.03