본문 바로가기
SideProject

스위프 5기 FADE 프로젝트 돌아보기 후기

by 배준오 2024. 8. 12.
반응형

들어가며

지난 6월 부터 8월 10일까지 스위그 5기 백엔드 개발자로 참여해 FADE 프로젝트를 출시했다.

주제 선정 기간 "매일 매일 패션왕을 뽑는 서비스가 있으면 어떨까?" 라는 내 의견으로부터 시작됐고 추가적인 피처를 구상하며 발전된 서비스이다. 다른 주제에 대한 의견도 많았는데, 심지어는 나조차 내 주제를 선택하는게 유치해 다른 주제를 선택했는데 출시 후 오늘 기준 유저가 50명까지 늘어났다니 이렇게 뿌듯하지 않을 수 없었다. 제일 마지막에 팀에 합류했고 부족한 점도 많았지만 6주 동안 함께 고생해주신 팀원 분들께 감사드립니다.

 

FADE: 투표로 만들어가는 패션 트렌드

당신의 패션 감각이 트렌드를 만듭니다! 매일매일 새로운 스타일에 투표하고, 가장 사랑받은 룩이 오늘의 페이피(FA:P)로 빛나요. 투표하며 발견한 나만의 취향, 한 눈에 모아보는 재미도 놓치지

fade.swygbro.com

 

팀 빌딩

스위프의 팀 매칭 과정은 좀 특이했다. 본인 소개 프로필을 만들고 Slack 메신저 DM을 통해 스스로 팀을 꾸려나가야 했다. 또한, 팀 매칭이 안된 개인은 스위프 활동에 참여할 수 없었다. 소개 프로필은 경험을 나열하고, 열정을 최대한 보여주는 느낌으로 간단히 작성을 했다. 매칭 시작 1시간 전 다른 사람들의 소개 프로필을 미리 볼 수있었는데 현직자들과, 실력자 분들이 많아 나같은 취준생은 팀 빌딩이 안될 수도 있겠다는 걱정을 했다. 다행히도 매칭 시작 1시간 뒤 프론트엔드 개발자 분과 동반 참가한 디자이너 분께 연락이 왔으며 팀의 마지막 멤버로 합류하게 되었다.

 

스위프 소개 카드 | Notion

👋 1. 자기 소개

ionian-alfalfa-532.notion.site

 

아이디에이션

아이디에이션은 1주간 진행했다. 목요일이 주제 선정 마감일이었고 그 전날인 수요일에 팀원들 모두 강남으로 모여 주제를 선정했다. 여러가지 주제가 있었지만 겉모습만 다를 뿐 대부분 루틴 관리 앱 느낌이였다. 빌드업을 하며 루틴 앱의 방향성, 특색을 정하기가 어려워 다른 주제를 찾던 중 '오늘의 패션왕' 을 기획자님께서 좀 재밌을거 같다며 의견을 나눠보자고 하셨다. 단순하고 재밌어 피처를 구상하기 쉬웠고 특색 또한 뚜렸했다. 아이디에이션 결과 '오늘의 패션왕' 은 데일리로 패션왕을 선정하고, 모인 데이터를 통해 데일리 패션 인사이트를 파악할 수 있는 서비스로 방향성이 잡혔다. 

 

MVP 회의

아이디에이션을 통해 구상한 피처를 기반으로 MVP 회의를 진행했다. MVP 회의를 진행한 이유는 실제로 개발 할 수 있는 기간이 3주 정도로 얼마 없었기 때문에 MVP 기능을 빨리 만들어야 했기 때문이다. 쳐낼건 확실하게 쳐냈다. 일반 로그인, 이메일 인증, 실시간 알림 기능, 신고 기능, 회원 탈퇴, 관리자 페이지 등 당장 없어도 상관 없는 기능들은 후순위로 밀어내고 1주일 간은 유저 인증, 카카오 로그인, 사진 업로드, 투표 기능, 예외 처리를 개발하기로 결정했다. 추가로 개발자들끼리 모여 결정한 MVP 기능에 대한 기술적인 논의와 예외 케이스를 정의했다.

  • 유저 인증 : JWT, AccessToken, RefreshToken 활용
  • JWT Payload : 이메일, 유저 PK 저장
  • 카카오 로그인 흐름 : 클라이언트에서 스프링 서버에게 인가 코드 넘겨주는 방식으로 진행
  • 리프레쉬 토큰 설계 : 로그인 시 AccessToken, RefreshToken 응답, RefreshToken으로 AccessToken 재발급 API
  • 사진 업로드 : AWS S3사용하여 정적 파일 관리, Presigned URL, CheckSum 알고리즘으로 중복 사진 처리
  • 피드 조회 : 무한 스크롤 기반 페이지 네이션
  • 이미지 리사이징 : CloudFront에 리소스 요청, Lamda 통해 캐싱 리사이징 된 리소스 반환
  • 투표 : 10개 투표 묶음으로 서버에 전달, 중복 투표 방지, 본인 피드 투표 방지
  • 예외 처리 : @RestControllerAdvice를 통한 커스텀 서버 예외 처리, @Valid 클라이언트 예외 처리 구분

 

개발

MVP 회의 후 REST API 기반으로 기능들을 만들기 시작했다. 익숙한 레이어드 아키텍처로 개발했고 나는 1주일 간

투표 관련 로직, 커스텀 예외처리 담당했고 PR - 코드 리뷰 - 병합 순으로 개발이 이뤄졌다. 생각보다 시간이 널널해서 아카이빙, 구독, 북마크를 개발했다. 다음 1주일은 조회 요청에 대한 메서드를 만들기 시작했다. 무한 스크롤 기반인데 구현 경험이 없어서 블로그를 엄청 찾아 봤던거 같다. 자료를 통해 이해는 됐는데 막상 구현을 하려고 하니 어려웠다. 다행히 백엔드 팀원 분 중 경험이 있으신 분이 계셔서 PR를 여러번 수정하고 코드 리뷰를 통해 도움을 많이 받았다. 알림은 실시간 푸쉬 알림이 아니다 보니 생각보다 구현하는데 어려운 점은 없었지만 비동기 요청을 처리하기 위해 구성 클래스에 @EnableAsync 붙혀서 사용했는데 ThreadPool을 잘 모르고 사용했어서 Custom Executor로 바꾸는 과정 중 개념을 이해하는데 좀 어려웠다.

// 알림 비동기 처리 Executor
@Configuration
@EnableAsync
public class AsyncConfig {

    @Bean("notificationAsyncExecutor")
    public Executor notificationAsyncExecutor() {
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        executor.setCorePoolSize(5);
        executor.setThreadNamePrefix("notification-async-executor-");
        executor.initialize();
        return executor;
    }
}

// 알림 이벤트를 Listen하고 있다가 비동기적으로 처리하는 알림 handler
@TransactionalEventListener
@Async("notificationAsyncExecutor")
public void handleNotificationTrigger(CreateNotificationDto createNotificationDto) {
    switch (createNotificationDto.type()) {
        case FEED_REPORTED:
            notificationService.createFeedReportedNotification(createNotificationDto);
            break;
        case FEED_DELETED:
            notificationService.createdFeedDeletedNotification(createNotificationDto);
            break;
        case FAP_SELECTED:
            notificationService.createFapSelectedNotification(createNotificationDto);
            break;
        case FAP_DELETED:
            notificationService.createdFapDeletedNotification(createNotificationDto);
            break;
    }
}
	
// 매일 Fap 선정 Schedular
@Transactional
@Scheduled(cron = "0 0 0 * * ?")
public void createFapArchiving() {
    FindMostVoteItemDto mostVoteItemDto = voteRepository.findMostVoteItem();
       if (mostVoteItemDto == null) {
            return;
        }

        final var feed = feedCommonService.findById(mostVoteItemDto.feedId());
        final var member = memberCommonService.findById(mostVoteItemDto.memberId());

        FapArchiving fapArchiving = FapArchiving.builder()
                .feed(feed)
                .member(member)
                .build();
        fapArchivingRepository.save(fapArchiving);
        
        // 알림 이벤트 발행
        notifyFap(fapArchiving, createFapNotificationDto(member.getId(), feed.getId()));
}
	
// 알림 목록에 저장
@Transactional
public void createFapSelectedNotification(CreateNotificationDto createNotificationDto) {
      final var receiver = memberCommonService.findById(createNotificationDto.receiverId());
      final var feed = feedCommonService.findById(createNotificationDto.feedId());

      notificationRepository.save(Notification.builder()
            .type(createNotificationDto.type())
            .isRead(false)
            .receiver(receiver)
            .feed(feed)
            .build());
}

API 연동

어느정도 개발이 마무리가 된 후 마감 3일 전 프론트 엔드 개발자 분과 맞춰 API 연동 작업을 진행했다. 정말 많은 버그가 발생했고 밤도 많이 샜다. 미리 API 연동을 마무리 했어야 했지만 초기에 정했던 DTO 명이 바뀐 경우도 있었고 데이터 반환에 대한 변경, API 설계 변경 이슈가 있어서 그렇게 하지 못했다. 정말 다행인게 프론트엔드 개발자 분께서 API Mocking 처리를 해두어서 백엔드에 이슈가 있어도 프론트는 이상이 없게 동작할 수 있었다. 또한 미리 구현해둔 CustomException이 API 연동하고 마감기한까지 버그를 수정하는데 많은 도움이 되었다. 

 

마무리

8/10일 d-camp 6층 홀에서 발표회를 가지며 프로젝트가 마무리 되었다. 팀원들과 강남역 근처에서 술 한잔하면서 그동안 하지 못했던 이야기도 하고 편하게 말도 놓으며 회포를 풀었다. 프로젝트 기간은 끝났지만 이후에도 FADE 서비스를 운영하며 개선점을 찾고 문제를 해결해 나가야겠다. FADE 팀원들 그동안 감사했고 앞으로도 화이팅 !

스위프 장점 + 혜택

1. 스위프에 참여하면 기획자, 디자이너 등 다양한 직군과의 협업 경험을 쌓을 수 있다.

2. 매주 리뷰데이땐 다른 팀들 발표를 들으며 개발적인 인사이트를 쌓을 수 있다.

3. 클라이언트 호스팅이 평생 무료이다.

반응형