[코드스쿼드] Max 19~20주차 - ToDo App 팀 프로젝트

Jinny·2023년 7월 24일
1

코드스쿼드 회고

목록 보기
13/13

Max 19주차: 23-07-10 ~ 23-07-14
Max 20주차: 23-07-17 ~ 23-07-21


✅ 두번째 팀 프로젝트

첫번째 프로젝트가 끝나고 1주일간의 방학을 갖고 두번째 팀프로젝트가 시작되었다.
두번째 팀 프로젝트는 Todo App을 만드는 것이었다.

하기에 첨부한 이미지는 Todo App의 메인페이지인데
Todo App은 흔히 알고 있는 Trello 같은 칸반 보드 형태의 To do List를 관리하는 프로그램이다.

메인 페이지

그리고 유저가 어떤 행동을 했는지 확인할 수 있는 사용자 활동 기록을 볼 수 있는 것이 Main Feature다.

사용자활동기록

보다 상세한 내용은 아래 깃허브 레포지토리에서 확인 가능하다.

Github: To do App 프로젝트



1. 🧐 회고

요번 글은 프로젝트 관련 고민 포인트에 대한 내용이 길어 회고는 간단하게 언급하고 넘어가려 한다.

1-1. 새로 공부한 내용

preflight

  • CORS 에러가 발생하면서 preflight에 대해 공부했다.

JWT

  • 로그인 기능을 구현하며 JWT에 대해 공부했다.
  • 하지만 아직 JWT 시그니처 암호화는 잘 이해하고 있지 못하다.
  • HS256 개념, 암복호화, 대칭키, 단방향/양방향 암호화에 대해 더 공부가 필요하다.
  • 어떤 상황에 세션 기반 인증을 하고 어떤 상황에 토큰 기반 인증을 선택하는지 공부가 더 필요하다.

1-2. 새로 시도해 본 것

Redis

  • 데이터/로그 캐싱을 위해 Redis를 처음 사용해봤다.

jayspt

  • application.properties 파일 암호화를 위해 jayspt을 사용했다.
  • CI/CD 사용 금지가 프로젝트 요구사항이어서 배포 스크립트만 작성했는데 스크립트에 복호화 암호를 쓰지 않고 복호화하는 것이 고민이었다.
    (스크립트를 깃허브에 올리거라 복호화 암호를 써놓으면 암호화 의미가 없기 때문)
  • 방법을 찾지 못해서 서버에 미리 application.yml 파일을 만들어 놓고 jar 파일 돌릴 때 다음과 같이 option을 추가했다.
  --Dspring.config.location=~/app/application.yml

슬라이싱

  • 조회 최적화 관련 고민을 하다가 팀원의 의견으로 활동 기록 조회 기능을 슬라이싱으로 구현해보았다.
  • 위에 gif 보면 스크롤 할 때마다 살짝 끊기는 느낌인데 그때 api 요청을 보내서 n개씩 데이터를 받아오는 식이다.
  • 자세히 쓰고 싶은데 캡쳐해둔 것이 없어서 살짝 언급하고 지나가겠다. 🥹

1-3. 아쉬운 점

요번 프로젝트는 공부해야 할 것도 많고
밑에 써놓은 고민포인트 논의 등으로 인해 시간이 엄청 엄청 부족해서 디테일을 많이 버려야 했다.

테스트 코드

  • 테스트 코드 작성을 아예 못했다.
  • JWT 테스트를 하려면 header에 유효한 토큰을 넣어줘야 API가 정상 작동 할텐데 어떻게 테스트해야 할지 고민해보지 못했다.

보안 관련 고민

  • 회원가입 시 비밀번호 암호화 등 보안 관련된 것은 아예 고민도 못 했다.

미구현 기능

  • JWT의 꽃은 로그아웃 기능이라고 느꼈는데 로그인 기능만 구현하고 로그아웃 기능은 구현 못했다.
  • 로그인을 여러번 하면서 토큰을 재발급 받으면 이전 토큰을 계속 사용할 수 있는 점이 문제가 없을까?에 대한 고민이 있었는데 깊게 생각하지 못했다.

1.4 프론트와 협업

두번의 팀프로젝트를 하면서 프론트(리액트)에 대해 협업 관련 이해도가 조금 생긴 것 같다.

리액트 build 결과는 정적 파일이다?(feat. S3 배포)
(잘못 이해한 부분이 있다면 댓글로 알려주세용 🥲)

  • 첫번째 프로젝트 때 프론트 서버를 어떻게 배포해야 할지 이런저런 고민을 하다가 EC2에 배포를 했다.
  • 프론트 서버 배포를 하면서 build한 파일이 main.ts인가 main.html인가 파일이 꼴랑? 하나 생겼던 것으로 기억한다.
  • 이때 프론트분들한테 질문을 했었는데 리액트의 특징이 화면이 새로 고침 되지 않고(새로운 url로 가서 화면을 새로 랜더링하는 것이 아니라) 현재 페이지에서 다른 페이지로 렌더링을 다시 해주는 방식이라고 알게되었다.
  • 그래서 요번에는 AWS S3의 정적 웹 호스팅 기능을 이용해 프론트 서버를 배포해보았다.
    (이해도가 없었으면 리액트가 왜 S3로 배포가 가능한지 이해하지 못했을 것 같다.)

컴포넌트는 상태 값을 갖고 있다.

  • 프론트에서 어떤 값을 들고 있고, 어떤 데이터를 보내줘야 하는지에 대한 감이 조..금 생긴 것 같다.

+)
해결하지 못한 이슈가 마음에 걸려 팀원인 Ape랑 프로젝트 끝나고 에러를 해결했다.
고쳐졌는지 확인하기 위해 프론트 코드를 하나씩 떼다가 퍼즐 조각을 맞춰 api 정상 응답을 확인했는데, 요 과정도 프론트에 대한 이해도를 높여준 것 같다.



2. 🤯 고민 포인트

프론트엔트, 백엔드 가릴 것 없이 요번 프로젝트의 최대 난제는
카드를 동일 컬럼 안에서 위 아래로 움직이는 기능다른 컬럼 간 이동하는 기능이었다.

이 이슈 하나로 고려해야 할 것이 굉장히 많았다.
우리 팀의 주된 고민은 다음 두가지였다.

  1. 카드 이동 로직을 어떻게 구현할 것인가?
  2. 카드 CRUD + 카드 이동 시 DB 접근이 빈번하게 일어나는데 어떻게 DB 접근 횟수를 줄일 수 있을까?

2-1. 카드 이동 로직

Linked List 방식

맨 처음에 나왔던 의견은 Linked List처럼 각 카드가 앞뒤 카드 id를 갖고 있고,
이동 시 Linked List 처럼 앞뒤 카드 id를 변경해주는 것이다.

해당 방법은 밑에 이유로 탈락했다.
(+ 효율적이지도 않은데 2번 3번 방식에 비해 드는 시간과 짜야하는 코드량이 몇배일 것으로 예상되었다.)

🤔 우려되는 점:

  • 정보 업데이트 시에는 소수 데이터만 변경하면 되지만, 메인 페이지에서 전체를 조회하게 되면 정렬을 해야 한다.
  • 그럼 카드 이동 시마다 매번 DB에서 데이터를 꺼내온 다음,
    전체 카드 정보를 탐색해서 전체 정렬을 해줘야 하는데, 효율적이지 않다.
    • 만약 유저가 계속 카드를 이동한다면?
    • 카드 수가 많이 늘어난다면?

② 클라이언트에서 전체 정보 전달

카드가 이동되면 각 카드의 id와 순서 정보를 통으로 서버에 전달해주면
DB에 해당 user의 전체 카드 순서 정보를 전부 업데이트하는 방법이다.

제일 원시적이지만 제일 간단한 방법이다.

하지만 해당 방법도 우려되는 점이 있었다.

🤔 우려되는 점:

  • 카드 이동 시, 카드 개수만큼 DB를 업데이트 해야 한다.
  • 이동 요청이 엄청 많다면?
  • 카드 개수가 엄청 많다면?
  • 회원이 엄청 많다면?

③ Positioning 방식 (정확히 어떤 방식으로 불리는지 모르겠으나 팀내에서 포지셔닝으로 불렀다.)

밑에 그림을 참고해보면 카드는 일정 간격의 가중치를 갖고 있고,
이동 시마다 위, 아래 카드의 평균값을 갖게 된다.

이렇게 되면 카드 이동 시 위아래 카드 가중치 값 조회를 위한 2개 데이터의 SELECT과,
이동할 카드의 포지션 값 변경을 위한 1개 데이터의 UPDATE만 하면 된다.


출처: 팀원 Ape가 그린 그림

이때 우려했던 점은 값이 점점 줄어 들다가
어느 시점이 되면 순서에 따른 값이 맞지 않는 순간이 올텐데
"이 순간"을 어떻게 체크하냐가 관건이었다.

여러 논의 끝에 카드 이동 시마다, 위의 카드의 가중치를 조회해서 1차이가 나면
전체 데이터의 간격을 1000으로 다시 조정하기로 했다.

이때 해당 컬럼의 카드 전체가 업데이트 되지만, 어쩌다 한번 발생하는 것이고
분할 상환 분석 이런 개념으로 보면 괜찮다는 생각이 들었다.

(개인적으로 수학을 못해서 가중치가 잘못되는 또다른 경우의 수가 있을 것 같아서 아직까지 조금 찝찝한 면은 있다.)

+) 카드 이동 로직 고민 시 참고했던 블로그

2-2. DB 접근 횟수를 어떻게 줄일 것인가?

카드 이동 로직과 제일 많이 고민하고 논의했던 부분은 "DB 접근 횟수를 어떻게 줄일 수 있는가?"였다.

카드 CRUD 와 카드 이동이 발생할 때마다 DB에 접근하고,
또 모든 액션이 발생할 때마다 사용자 활동 기록을 보여주기 위해 로그를 DB에 쌓아야 했다.

로컬 스토리지에 데이터를 일정 시간 저장 후 서버에 요청하는 등 다양한 방식을 고려했으나
결론적으로는 write back 방식의 Redis를 사용해서 데이터를 캐싱하기로 했다.
(JWT 토큰을 이용한 로그인 기능이 요구사항에 있어서, TTL을 설정해 Refresh token과 만료된 Access Token을 저장/처리하기 위함도 있었다.)


...라는 원대한 꿈을 꾸었지만 결론적으로 서버 배포 한번 해보고 Redis 관련 코드를 모두 들어냈다.

이유는 카드 이동 로직 관련 논의에 시간을 많이 쏟기도 했고,
JWT도 새로 공부해서 구현해야 했는데 Redis에 대한 이해도 부족과 공부할 시간 부족으로
낯선 에러를 처리할 수가 없었다... 🥲

나중에 참고차 에러 메시지를 적어두려 한다.

에러메시지 1

  • left pop을 할 때 데이터가 없으면 발생하는 것으로 추측된다.
  • 그런데 Redis 데이터를 확인했을 때 데이터가 들어있었다.
Error in execution; 
nested exception is io.lettuce.core.RedisCommandExecutionException: 
ERR wrong number of arguments for 'lpop' command

에러메시지 2

  • 구글링해보면 @scheduled 어노테이션이랑 @transactional 어노테이션이랑 같은 클래스에서 사용하면 나타나는 에러라고 한다.
  • 확인차 어노테이션 하나를 제거했는데도 동일한 에러가 발생했다.
  • 그리고 두 어노테이션을 같이 쓰면 안되는 이유에 대해서도 공부해봐야 할 것 같다.
Unexpected error occurred in scheduled task

Redis는 다음에 다시 시도해 보는 것으로!!



3. 🔥 Trouble Shooting

요번 프로젝트를 진행하면서 Trouble Shooting한 내용은 블로그에 별도로 정리해놓았다.

3-1. CORS

  • 로그인 filter 기능 구현 후 CORS 설정이 먹히지 않다가 이런저런 설정을 하면서 해결이 되긴 했는데 원인을 정확히 모르겠다.
  • Spring이랑 Http 흐름에 대해 공부를 더 해봐야 할 것 같다.

[Spring] filter 기능 구현 후 CORS 에러가 발생하는 이슈

3-2. JWT

  • JWT 토큰 파싱하면서 오류가 있어서 삽질을 반나절했는데 대단한(?) 원인은 아니었다.
  • 개발자는 눈을 크게 떠야 한다.

[JWT] SignatureException 에러

3-3. S3

  • S3 정적 호스팅을 기능을 처음 경험해봤는데, 새로운 기능을 알게되어서 좋았다.

[AWS] S3 정적 호스팅 페이지 새로고침 시 404 NoSuchKey 에러가 발생하는 이슈



4. 🧹 세 줄 정리

  • 투두앱 쉽게 보다가 카드 이동에 큰코 다쳤다.
  • 구현 못한 기능과 디테일을 챙기지 못한 점이 아쉽다.
  • 블로그 글을 쓰면서 원인을 모르겠는 것들이 많아 공부가 필요하다고 적어놓은 것이 많다.
    공부를 한다고 하지만 기술 부채가 쌓이고 있어서 반성해야겠다.
profile
블로그 이사갔어요. https://jinny-l.tistory.com/

5개의 댓글

comment-user-thumbnail
2023년 7월 24일

정보 감사합니다.

답글 달기
comment-user-thumbnail
2023년 7월 24일

이렇게 글로 보니 저희가 2주간 경험한 게 정말 많네요. 이래서 회고를 쓰는구나! 싶습니다. 본받고 싶어요. 같은 조 돼서 즐거웠습니다. 👋

1개의 답글
comment-user-thumbnail
2023년 7월 24일

집가는 길에 공감이 많이 가는 글을 읽네요 저도 jwt, 보안 관련해 고민이 많았는데 남 일 같지않네요 항상 프로젝트나 무언가를 끝내고 회고를 남겨야지 하면서도 쉽지않았는데 많이 배우고갑니다!!

1개의 답글