BackEnd Developer, Love Crossfit, Welcome to Jay's blog

프로그래머스 웹백엔드 스터디 7기 / 4주차 - 비즈니스 로직 2 댓글 구현과 S3 이미지 업로드 비동기 처리

|

프로그래머스 웹백엔드 스터디 7기 활동을 하면서 공부한 내용과 기능 구현시 했던 고민들을 정리한 내용입니다.


Index

  • 스터디 4주차
    • 느낀점
    • 고민
    • 공부한 내용
    • TODO 기능 구현
  • References

스터디 4주차

비즈니스 로직 구현 2 - S3 이미지 업로드 비동기 처리와 댓글 구현

느낀점

이번주도 2주차처럼 기술적으로 어려운 미션들은 아니었지만, AWS S3 업로드 예외가 발생했을 때 어떻게 처리할 건지, 로그는 어떻게 어떤 내용을 남길 건지에 대한 고민을 하게 되었다. 여기서는 프로필 업로드를 회원 가입할 때 진행하였는데, 업로드 과정을 CompletableFuture를 이용해 비동기처리로 하는 과정이 흥미로웠다. 이외에도 수많은 메소드들이 있다고 리더분께서 말씀해주셨는데, 다음에 자바 비동기처리에 대해 좀 더 공부를 해봐야겠다.

고민

  • 회원 가입시 필요한 프로필 사진 업로드. 업로드 실패시 예외를 무시하고 가입 처리 시켜야 하나? 아니면 가입 실패로 간주해서 예외를 던져야 하나?

    • 수많은 SNS들을 생각해보면 기본 프로필 사진이라는게 존재한다. 즉, 프로필 이미지는 필수가 아니고 실패해도 비즈니스적으로 문제가 크지 않기 때문에 예외를 무시하고 회원 가입을 완료시켰다.
  • S3 업로드 중 발생하는 Exception은 어떻게 처리하고 로깅은 뭐로 남겨야하지?

    • SdkClientException, AmazonServiceException 같이 AWS S3에서 오류가 발생한 경우 즉각 대응이 어렵고, 해당 서비스가 정상이 될때까지 기다려야 하므로, 로그 레벨 error보다는 warn이 맞을 꺼라 판단해서 warn으로 로깅을 남겼다. 그리고 예외는 try-catch를 이용했다

공부한 내용

  • AWS S3(Simple Storage Service)
    • 웹하드와 비슷. 인터넷상에 data를 올리고 다운 받는 곳.
    • 웹하드와의 차이점
      • Rest API로 저장/삭제/조회가 가능
      • 저장 용량 제한 없음
      • 99.99% 이상의 내구성 => 유실율 없음
    • 크기가 매우 큰 파일 업로드시 사용 ex)대용량 로그파일
    • 가격 싼 편
    • 용어
      • 객체(Object): S3에 데이터가 저장되는 최소 단위. 업로드되는 파일, 이미지
      • 버킷(bucket): 업로드한 파일이 위치하게될 최상위 폴더(디렉토리)
      • 리전(Region): 내가 올린 객체가 저장될 물리적 위치, 리전 간 객체 공유는 불가능
        • ap-northeast-2: 한국 리전 의미
    • AWS S3 환경 세팅
      • pom.xml 파일에 dependency 추가 - aws-java-sdk
      • AWS S3 인증 정보 configure 클래스 생성과 S3 이용을 위한 설정 파일 주입
        • AWSConfigure 설정 클래스
    • AWS S3 사용
      • 객체 업로드
        • PutObjectRequest 객체 사용해 업로드 요청
        • 업로드 결과는 PutObjectRequest 객체 사용해 확인
      • 객체 조회
        • AWS SDK에서 제공하는 S3 클라이언트의 getObject 메소드 사용
        • 또는 URL 사용해 조회
          • 기본 URL + bucket 명 + 객체 Key
          • https://s3.ap-northeast-2.amazonaws.com/버킷이름/key.jpeg
    • AWS S3 연동 예외처리
      • S3란 네트워크 통해 업로드 하는 기능 -> 언제든 예외 발생 가능함을 인지해야함
      • 그런데 네트워크 I/O와 파일 I/O 같은 I/O 작업은, 예외가 언제 발생할지 예측할 수 없는 근본적으로 막을 수 없는 작업!
      • 다시 connection 맺어서 될때까지 retry 하는 정도의 대응 밖에 없음
      • 그러므로 S3 연동시 반드시 방어 로직 추가해줘야함!!!
      • 보통 RuntimeException을 발생시키기 때문에 try-catch 사용 안함
      • AWS S3 작업으로 발생한 예외는 비즈니스 로직에 영향을 주지 않도록 하는 것이 중요
        • 사용자 프로필 이미지 업로드 실패시 회원 가입 처리가 실패해야 하는가? - 무엇이 자연스러운 flow일까
  • 예외처리 가이드
    • 상황에 맞는 올바른 예외 클래스를 사용하자
      • RuntimeExceptio(Unchecked Exception)
        • try catch 할 필요 없음. 요즘에 이걸 더 선호함
        • 단점. 익숙해지면 예외 잡아야하는 경우에도 안 잡고 넘어가게 될 수가 있음.
        • return type, 파라미터에 무엇이 있고 언제 어떤 예외가 발생할 수 있는지 확인하는 습관 필요
      • CheckedException
        • try catch로 반드시 감싸야 하는 예외
        • ex. JDBC 예외. 네트워크,파일 I/O 예외
        • IDE에 빨간줄 안 떠도 예외처리는 미리 해줘야함. 언제 발생할지 모르니
      • DB 관련 예외는 Repository 레이어에서, 비즈니스 로직 관련 예외는 Service 레이어에서 발생시키기
    • 예외 로깅
      • 예외를 catch 했다면 반드시 로그 남기기. 어떤 로그 남길지 고민 필요
      • 경우에 따라 예외 로깅 제외 가능 - ex. 디버깅이 필요없고 너무 많이 발생하는 경우
      • 로깅 프레임워크 활용해 디버깅에 도움되는 정보와 함께 stack trace를 함께 남긴다.
        • log.error(“Unexpected error from userId: {} : {}”, userId, e.getMessage(), e);
    • 로그 파일 관리
      • 생각 없이 로그를 파일에 남기기만 하면 DISK Full 장애가 날 수 있고, 하나의 로그 파일 크기가 너무 큰 경우 필요한 정보 찾는데 시간이 많이 걸림
      • 따라서, 단일 로그 파일 크기를 적절한 크기로 제한하고, 보통 50~100MB, 오래된 건 지우고 최근 며칠 것 만 보관한다.
      • 일반적으로 사용하는 로깅 프레임워크에서 관련 설정 모두 지원 - log4j2, logback, sl4j 등등
  • Java에서 비동기 처리 방법 - CompletableFuture

    • 굉장히 많은 메소드들이 있지만 그 중 CompletableFuture와 자주 사용하는 메소드에 대해 알아보겠다.
    • CompletableFuture
      • 함수형 프로그래밍 기법을 지원하는 비동기 병렬 프로그래밍 지원
      • 명시적 Thread Pool 선언 필요 없음
      • 함수형 프로그래밍 방식을 지원하므로 간결하고 가독성 좋은 코드 작성 가능
      • 각 병렬 Task들의 손쉬운 결합. 예외처리 지원
    • CompletableFuture의 메소드

      • supplyAsync
        • static CompletableFuture runAsync (Runnablerunnable)
        • 반환값이 필요 없을 때 사용
      • runAsync

        • static <U> CompletableFuture <U> supplyAsync(Supplie<U> supplier)
        • 반환값이 필요할 때 사용. 체이닝 가능
        • thenApply
          • 비동기 작업의 결과값을 새로운 값으로 변환하는 함수 실행
          • <U> CompletableFuture<U> thenApply(Function<? super T, ? extends U> fn)
          • T: 비동기 작업의 결과값, U: T를 U로 변환해서 반환
        • thenAccept
          • 비동기 작업의 결과값을 소비하고 끝
          • CompletableFuture<Void> thenAccept(Consumer<? super T> action)
        • exceptionally
          • 체이닝된 구문에서 예외 발생시 수행되며, 결과값 T 생성
          • CompletableFuture<T> exceptionally(Function<Throwable, ? extends T> fn)
      • allOf
        • 동시에 N개의 비동기 작업을 실행하고, N개 작업 모두 완료된 후에 진행
          • static CompletableFuture<Void> allOf(CompletableFuture<?>..cfs)
      • anyOf
        • 동시에 N개의 비동기 작업을 실행하고, 하나라도 작업이 완료되면 진행
          • static CompletableFuture<Object> anyOf(CompletableFuture<?>..cfs)

TODO 기능 구현

1. User, ConnectedUser에 String타입의 profileImageUrl 필드 추가

2. 회원가입시 업로드한 프로필 이미지를 S3에 업로드(CompletableFuture를 사용해 비동기처리)

  • S3에 업로드 후 오브젝트 접근을 위한 URL을 profileImageUrl에 추가
  • S3 예외 발생에 대한 처리 및 그 이유 설명

3. Comment 관련 비즈니스 로직구현

  • Comment 작성 API 및 로직 구현
    • Comment가 작성되면 관련 post의 comments가 1 증가해야 함
  • 특정 post에 대한 comment 목록 조회 API 및 로직 구현
  • comment 관련 로직은 가능한 stream api를 활용해 fluent 스타일로 코드 작성
  • 가능한 도메인 모델을 활용해 코드 작성

4. CommentService 테스트작성

5. 새로운 RESTAPI 및 관련 모델에 대해 Swagger를 통해 문서화

References

  • 초보 웹 개발자를 위한 스프링 5 프로그래밍 입문
  • 토비의 스프링 3.1
  • 처음 배우는 스프링 부트 2