Book & Diary Hub 프로젝트

aydennote·2023년 6월 10일
0

Project

목록 보기
7/8
post-thumbnail

프로젝트 회고

1. 사용자 모델과 게시물 모델 관계 설정

사용자별 게시물 데이터를 가져오는 방법을 고민하면서 두 가지 접근 방법을 고려했다.
첫 번째 방법은 사용자 모델과 게시물 모델 관계 설정으로 사용자에게 맞는 게시물을 불러오는 방법이다.
두 번째 방법은 사용자 아이디를 파라미터로 전달하고 게시물 데이터를 필터링하는 방법이다.

첫 번째 방법은 프로젝트의 확장성과 데이터 무결성을 강화하는 장점을 가지고 있다. 그러나, 현재 프로젝트의 설계상 사용자는 다른 사용자의 게시물을 볼 수 없도록 제한되어 있기 때문에 데이터 조회 성능 측면에서는 두 가지 접근법 간 큰 차이가 없을 것으로 판단했다. 또한, 전반적인 웹 개발 경험을 하되 프론트엔드 학습이 중점인 프로젝트이기에 간단한 두 번째 방법을 택하여 구현하는 것으로 결정했다.

2. DB에 게시물 이미지 저장 방법

게시물 이미지를 효과적으로 관리하기 위한 방법을 학습하고 적용했다. multer 라이브러리를 활용하여 서버에 게시물 이미지를 저장하고, 이에 대한 경로를 데이터베이스에 저장하는 방법을 선택했다. 클라이언트에서는 이미지를 요청하면 서버에서 해당 경로에 있는 파일을 가져와 클라이언트에게 전송하는 방식을 활용했다.

3. 토큰 만료

토큰이 만료될 때 마다 다시 로그인해서 토큰 재발급 받고 클라이언트에 다시 저장해야되는 궁금하여 알아보고 access 토큰과 refresh 토큰을 적용하게 되었다.

  • 기존 토큰 발급 및 인증 방식.
  1. 로그인 시 토큰 발급.
  2. 클라이언트에 토큰 저장.
  3. 클라이언트에 저장된 토큰과 함께 서버 API 호출.
  4. 서버에서 토큰 인증.
  5. 토큰이 만료된 경우, 해당 API 호출은 에러가 발생되며 사용자에게 재로그인 안내.
  • access, refresh 토큰 발급 및 인증 방식
  1. 로그인 시 access, refresh 토큰 발급.
  2. DB에 refresh 토큰만 저장, 클라이언트에는 두 토큰 모두 저장.
  3. 클라이언트에 저장된 토큰과 함께 서버 API 호출.
  4. 서버에서 access 토큰 인증.
  5. 토큰이 만료된 경우, 클라이언트에 저장된 refresh 토큰과 함께 서버 refresh 토큰 인증 API 호출.
  6. 서버에서 DB와 클라이언트에 저장된 refresh 토큰을 비교 후 인증.
    6-1. 인증에 성공하면 access 토큰만 재발급 및 3번 과정에서 호출한 API 재호출.
    6.2. 인증에 실패하면 사용자에게 재로그인 안내.
  • 스스로 QnA
  1. 모든 요청에 access 토큰과 refresh 토큰을 서버로 보내고 각 토큰 인증 미들웨어를 통해 토큰을 인증하는 방식이 코드 간결성, 보안, 서버 성능면에서 더 괜찮은 방식인지. 아니면 요청에 access 토큰 하나를 서버로 보내고 만료 시 access 토큰 재발급 요청을 refresh 토큰과 함께 서버로 보내 인증하는 방식이 더 괜찮은지 궁금했다.
    현재 적용된 방식은 일반적인 경우 access 토큰만 보내는 것인데 만료 시 access 토큰 재발급을 위해 서버에 요청을 따로 한 번해야 한다.
    "만약, 두 토큰을 같이 보내면 따로 요청 없이 재발급이 가능하지 않을까? "라는 생각을 하게 되었다.

    코드 간결성 :
    클라이언트측 코드 간결성 및 유지보수성은 두 방식 모두 토큰 인증 에러에 대한 예외처리 방식은 비슷하기 때문에 큰 차이가 없다고 생각된다.
    하지만, 두 토큰을 모두 보내는 방식의 경우 서버측에서는 모든 요청에 access와 refresh 토큰을 인증하고 만료 시 재발급하는 과정이 있기 때문에 간결성은 떨어지고 의존성은 높아질 수 있다.

    서버 성능 :
    access 토큰만 보내는 방식은 access 토큰 만료 시 access 토큰 재발급 요청이 발생한다는 점이 있다. 다만, access 토큰 만료될 때 한 번 요청이 추가되는 것이라 서버 성능에 영향이 있지는 않을 것 같다.
    access와 refresh 토큰을 같이 보내는 방식은 서버에서 매번 두 토큰을 인증해야 하기 때문에 요청이 빈번한 경우 성능 저하가 발생할 수 있다.

    보안 :
    두 토큰 모두 보안을 위해 최소한으로 노출하는 것이 좋다. 따라서, 모든 요청에 두 토큰을 보내는 것보다. access 토큰만 보내고 만료 시 refresh 토큰을 보내는 방식이 적절하다고 생각한다.

  • 결과적으로 내가 고려한 부분에 있어서 현재 프로젝트에는 일반적인 경우 access 토큰만 보내는 방식이 더 적절하다고 생각된다.

4. 도서 감상문과 일기 생성, 삭제, 수정 컴포넌트

생성, 삭제, 수정 컴포넌트를 도서 감상문 페이지와 일기 페이지에서 재활용하고자 했다. 스타일은 동일한 유지하면서도 감상문과 일기 페이지에서는 서로 다른 기능이 수행되어야 했다. 예를 들어, 감상문 페이지에서는 감상문 API 호출이 이루어져야 하고 일기 페이지에서는 일기 API 호출이 이루어져야 하는 차이가 있다.

이를 해결하기 위해 URL 주소와 조건문을 활용하여 각 페이지의 컴포넌트 기능을 구분했다. 이러한 방식으로 프론트엔드에서 아래 코드 처럼 props와 URL path를 활용하여 각 페이지와 컴포넌트별로 적절한 기능을 구현했다.

const Add = ({ type }) => {
  const navigate = useNavigate();
  const pathName = useLocation().pathname;

  // 페이지별 이동 경로 
  const handleAddPost = () => {
    if (pathName === '/') {
      navigate(`${type}/write`);
    } else {
      navigate('write');
    }
  };

  // 페이지별 텍스트
  return <AddBtn onClick={handleAddPost}>{type === 'report' ? '게시글 생성' : '일기 생성'}</AddBtn>;
};

5. 서버 파일 업로드 데이터 파싱

클라이언트에서 formData로 보낸 파일을 서버 req.body에 데이터가 있는지 확인 했을 때, {}로 출력이 되었다.

파일 업로드와 관련된 데이터는 기본적으로 노드 서버에서 파싱되지 않기 때문에 클라이언트에서 formData 형식으로 보낸 파일은 서버의 req.body에 데이터가 없는 상태로 나타난다. 따라서, 파일 업로드를 처리하기 위해서는 별도의 미들웨어나 라이브러리를 사용하여 데이터를 파싱해야 한다.

해당 프로젝트에서는 multer 미들웨어를 활용하여 데이터를 파싱하고 파일을 처리했다. multer를 사용하면 파일 데이터를 파싱하여 서버에 데이터를 저장하고 관련 추출할 수 있다.

6. 깃허브 디렉토리 대문자

평소에 집 데스크탑에서 개발하다가 외부에서 노트북으로 개발하려고 했을 때, 수많은 에러가 발생되었다.

수많은 에러는 컴포넌트 파일이 없다는 것으로 컴포넌트 폴더 안에 하위 폴더들 몇개가 대문자로 적용되어 있는 것을 확인했다.
지난 팀 프로젝트 협업에서는 이러한 문제가 없었는데 인터넷을 검색해보니 깃은 따로 설정하지 않으면 기본적으로 파일 대소문자를 무시한다고 한다.
해결 방법은 깃 명령어로 대소문자 인식에 대해 설정해주면 된다.
git config core.ignorecase false

7. 서버에 저장된 사용하지 않는 이미지 삭제

게시물 이미지를 교체하게 되면 변경 전 이미지는 서버에 계속 남아 있어 서버 성능 저하 문제가 발생되고, 기존 게시물 이미지를 삭제하는 방법에 대해 아래 두 가지 방법을 고려해보았다.

  1. 이미지가 남지 않도록 게시물 이미지 수정 시 기존 이미지를 삭제하는 방법.
  2. 게시물 이미지를 읽어 오거나 수정하거나 등등 이미지에 접근하면 db에 접근 날짜를 업데이트하고 오랫동안 접근하지 않은 이미지를 서버에서 스케줄을 등록해 주기적으로 삭제하는 방법.

첫 번째 방법은 기존 이미지를 바로 삭제하므로 불필요한 이미지가 서버에 누적되지 않는다. 하지만, 이미지를 자주 변경하게 되면 서버 디스크 I/O가 증가하여 서버 성능 저하를 야기시킬 수 있다.

두 번째 방법은 스케줄링을 통해 주기적으로 불필요한 이미지를 삭제할 수 있어 자동화된 이미지 정리가 가능하다. 하지만, 게시물 이미지에 접근할 때마다 날짜를 업데이트하는 작업과 주기적으로 서버에서 오래된 파일을 삭제하는 작업이 추가로 필요하고 이로 인해 db에 부하가 발생할 수 있다.
또한, 사용자가 오랜만에 게시물을 보았을 때 이미지가 삭제되어 있어 에러가 발생할 수 있는 리스크가 있다.

내가 선택한 방법은 첫 번째 해결 방법으로 이유는 간단한 구현 방식(코드 복잡도 감소)과 해당 프로젝트 규모를 생각했을 때, 큰 리스크가 없다는 것 때문이다.

8. 게시물 생성 날짜 계산

게시물을 생성할 때, 클라이언트에서 날짜를 전달하는 방법과 서버에서 게시물 저장 시 날짜를 계산하여 저장하는 방법 중 어떤 것이 더 적절한지에 대한 고민을 했었다.

클라이언트에서 날짜를 서버로 보내면 조작할 수 있기 때문에 보안성이 떨어진다는 단점이 있지만 클라이언트 날짜로 저장된다는 장점이 있다. 결국, 사용자 기준으로 날짜를 생성하는 것이 더 적합하다고 생각하여 클라이언트에서 날짜를 보내는 방법으로 구현했다.

9. 서버 통신 에러

웹 페이지와 서버를 실행한 채로 두고 있었는데 서버 통신이 끊어져 서버에 에러 발생이 확인되었다. 이전에 클라이언트에서 서버로의 요청 내역이 없었기 때문에 실제 에러의 원인을 파악하기 어려웠다. 이러한 상황에서 정확한 에러 원인을 파악하기 위해 서버 로그를 구현하는 것이 필요할 것 같다.

pm2를 사용하여 로그를 확인하고 있으며, 이전에 발생했던 오류는 서버를 npm run dev로 시작했을 때, 절전모드로 변경된 PC에서 네트워크가 끊어져 서버까지 중단된 것으로 확인되었다.

10. 배포

여러 배포 옵션을 고려 후 서버는 AWS EC2를 사용하여 배포하고 클라이언트는 기존에 사용해본 Netlify나 AWS S3를 이용하여 배포하는 것으로 계획했다. AWS S3로 배포를 시도했을 때, 환경 변수 설정 문제가 있었는데 Netlify의 경우, 간단히 환경 변수를 설정할 수 있는 탭이 있어 key와 value를 입력하면 되지만 S3는 환경 변수 설정 기능이 별도로 없었고, 이에 대한 문제를 해결하기 위해 다시 Netlify로 배포하기로 결정했다.

배포 과정은 원활하게 진행될 것으로 예상되었으나, 여기서 또 다른 문제가 발생했는데 Netlify는 HTTPS를 사용하고 있었고 EC2 서버는 HTTP로 설정되어 있었다. 서버를 HTTPS로 변경하는 방법이 있었지만 번거로운 도메인 등록 및 설정 과정을 피하기 위해 다시 AWS S3로 배포하기로 결정했다. 환경 변수 문제는 .js 파일에 환경 변수들을 직접 담아 S3 버킷과 함께 업로드하는 방식으로 해결했다.

Github Action을 이용해 CICD 파이프라인을 구축한 뒤에는 기존 .js 환경 변수를 담아 버킷과 함께 업로드 하는 방식에서 환경변수를 .yml 파일에 작성하는 방법으로 개선하여 버킷에 업로드되는 불필요한 리소스를 줄이고 유지보수성을 향상시킬 수 있었다.

11. CORS

이번 프로젝트에서도 어김없이 CORS 에러가 발생했다.
1. 첫 번째 CORS 에러
에러 메시지를 보면 다른 HTTP 메서드에는 정상 작동하고 PATCH 메서드만 문제가 있다는 걸 알 수 있는데, 찾아보니 AWS에서는 PATCH 메서드를 지원하지 않는다고 한다. (제가 잘못 알고 있다면 댓글 부탁드립니다.🙇‍♀️)

AWS S3 버킷 CORS 정책을 편집하려고 했으나, Found unsupported HTTP method in CORS config. Unsupported method is PATCH 에러로 인해 PATCH 메서드를 AllowedMethods에 추가하는 것이 불가능했다. 결국, 기존에 PATCH로 작성된 부분을 POST로 변경하여 에러를 해결했다.

  1. 두 번째 CORS 에러

도서 정보 API 호출 시 CORS 에러가 발생했다. http://cors-anywhere.herokuapp.com/ 사이트에서 데모 서버를 통해 요청을 우회하여 임시로 해결할 수 있었는데 이 데모 서버는 언제 닫힐지 모르기 때문에 신뢰성이 낮다는 단점이 있었다.

이미 노드 서버를 만들었기 때문에 해당 서버를 통해 직접적인 CORS 에러를 해결하는 방법을 생각했고 이를 통해 도서 정보 API 호출 기능을 서버에 구현하여 문제를 해결했다.

12. 테스트 코드 작성

해당 프로젝트에서는 Jest와 React Test Library(RTL)를 이용하여 테스트 코드를 작성했다.

Jest는 모의 객체(Mock)을 지원하기 때문에 코드의 일부를 격리된 환경에서 테스트하고 코드의 견고성 및 정확성을 검증할 수 있어 유닛 테스트에 적합하다.

RTL은 render 함수를 사용하여 컴포넌트를 렌더링하고, screen 객체를 사용하여 화면 요소를 선택하고 검증하는 기능이 있어 실제 사용자 경험과 유사한 테스트에 적합하다.

두 테스트 도구 모두 위와 같은 장점으로 혼합하여 사용하게 되었다.

13. S3 CICD 배포

Github Action을 사용하여 git push 후 테스트 및 배포 자동화 파이프라인을 구축했다. 서버 URL을 정의하는 환경 변수명을 PUBLIC_URL로 사용하고 있었는데, 나중에 알게 된 사실은 public/index.html 파일의 link 태그에서 이미 PUBLIC_URL 변수를 사용하고 있다는 것이었다. 실수로 중복으로 사용한 부분이 있었는데, 이로 인해 배포 시 문제가 발생했다. 문제를 해결하기 위해 서버 URL 환경 변수명을 수정한 뒤 웹 페이지가 정상적으로 렌더링되었다.

아쉬운 점

  1. git 하나의 레포지토리에 client, server를 같이 넣었는데 CICD를 구축하다 보니 아래와 같은 문제점이 있었다.
  • 빌드 및 배포 프로세스 복잡성: 클라이언트와 서버 각각 다른 빌드 및 배포 프로세스가 있는데 이로 인해 CICD 파이프라인을 구축할 때 프로세스를 분리하고 관리하는 것이 더 복잡해졌다.
profile
기록하는 개발자 Ayden 입니다.

0개의 댓글