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 프로젝트
요번 글은 프로젝트 관련 고민 포인트에 대한 내용이 길어 회고는 간단하게 언급하고 넘어가려 한다.
preflight
preflight
에 대해 공부했다.JWT
JWT
에 대해 공부했다.JWT
시그니처 암호화는 잘 이해하고 있지 못하다.Redis
Redis
를 처음 사용해봤다.jayspt
application.properties
파일 암호화를 위해 jayspt
을 사용했다.CI/CD 사용 금지
가 프로젝트 요구사항이어서 배포 스크립트만 작성했는데 스크립트에 복호화 암호를 쓰지 않고 복호화하는 것이 고민이었다.application.yml
파일을 만들어 놓고 jar
파일 돌릴 때 다음과 같이 option
을 추가했다. --Dspring.config.location=~/app/application.yml
슬라이싱
요번 프로젝트는 공부해야 할 것도 많고
밑에 써놓은 고민포인트 논의 등으로 인해 시간이 엄청 엄청 부족해서 디테일을 많이 버려야 했다.
테스트 코드
보안 관련 고민
미구현 기능
두번의 팀프로젝트를 하면서 프론트(리액트)에 대해 협업 관련 이해도가 조금 생긴 것 같다.
리액트 build 결과는 정적 파일이다?(feat. S3 배포)
(잘못 이해한 부분이 있다면 댓글로 알려주세용 🥲)
build
한 파일이 main.ts
인가 main.html
인가 파일이 꼴랑? 하나 생겼던 것으로 기억한다. S3
의 정적 웹 호스팅 기능을 이용해 프론트 서버를 배포해보았다.컴포넌트는 상태 값을 갖고 있다.
+)
해결하지 못한 이슈가 마음에 걸려 팀원인 Ape랑 프로젝트 끝나고 에러를 해결했다.
고쳐졌는지 확인하기 위해 프론트 코드를 하나씩 떼다가 퍼즐 조각을 맞춰 api 정상 응답을 확인했는데, 요 과정도 프론트에 대한 이해도를 높여준 것 같다.
프론트엔트, 백엔드 가릴 것 없이 요번 프로젝트의 최대 난제는
카드를 동일 컬럼 안에서 위 아래로 움직이는 기능
과 다른 컬럼 간 이동하는 기능
이었다.
이 이슈 하나로 고려해야 할 것이 굉장히 많았다.
우리 팀의 주된 고민은 다음 두가지였다.
- 카드 이동 로직을 어떻게 구현할 것인가?
- 카드 CRUD + 카드 이동 시 DB 접근이 빈번하게 일어나는데 어떻게 DB 접근 횟수를 줄일 수 있을까?
① Linked List
방식
맨 처음에 나왔던 의견은 Linked List
처럼 각 카드가 앞뒤 카드 id를 갖고 있고,
이동 시 Linked List
처럼 앞뒤 카드 id를 변경해주는 것이다.
해당 방법은 밑에 이유로 탈락했다.
(+ 효율적이지도 않은데 2번 3번 방식에 비해 드는 시간과 짜야하는 코드량이 몇배일 것으로 예상되었다.)
🤔 우려되는 점:
- 정보 업데이트 시에는 소수 데이터만 변경하면 되지만, 메인 페이지에서 전체를 조회하게 되면 정렬을 해야 한다.
- 그럼 카드 이동 시마다 매번 DB에서 데이터를 꺼내온 다음,
전체 카드 정보를 탐색해서 전체 정렬을 해줘야 하는데, 효율적이지 않다.
- 만약 유저가 계
속 카드를 이동한다면?- 카드 수가 많
이 늘어난다면?
② 클라이언트에서 전체 정보 전달
카드가 이동되면 각 카드의 id와 순서 정보를 통으로 서버에 전달해주면
DB에 해당 user의 전체 카드 순서 정보를 전부 업데이트하는 방법이다.
제일 원시적이지만 제일 간단한 방법이다.
하지만 해당 방법도 우려되는 점이 있었다.
🤔 우려되는 점:
- 카드 이동 시, 카드 개수만큼 DB를 업데이트 해야 한다.
- 이동 요청이 엄청 많다면?
- 카드 개수가 엄청 많다면?
- 회원이 엄청 많다면?
③ Positioning 방식 (정확히 어떤 방식으로 불리는지 모르겠으나 팀내에서 포지셔닝으로 불렀다.)
밑에 그림을 참고해보면 카드는 일정 간격의 가중치를 갖고 있고,
이동 시마다 위, 아래 카드의 평균값을 갖게 된다.
이렇게 되면 카드 이동 시 위아래 카드 가중치 값 조회를 위한 2개 데이터의 SELECT
과,
이동할 카드의 포지션 값 변경을 위한 1개 데이터의 UPDATE
만 하면 된다.
출처: 팀원 Ape가 그린 그림
이때 우려했던 점은 값이 점점 줄어 들다가
어느 시점이 되면 순서에 따른 값이 맞지 않는 순간이 올텐데
"이 순간"을 어떻게 체크하냐가 관건이었다.
여러 논의 끝에 카드 이동 시마다, 위의 카드의 가중치를 조회해서 1차이가 나면
전체 데이터의 간격을 1000으로 다시 조정하기로 했다.
이때 해당 컬럼의 카드 전체가 업데이트 되지만, 어쩌다 한번 발생하는 것이고
분할 상환 분석 이런 개념으로 보면 괜찮다는 생각이 들었다.
(개인적으로 수학을 못해서 가중치가 잘못되는 또다른 경우의 수가 있을 것 같아서 아직까지 조금 찝찝한 면은 있다.)
+) 카드 이동 로직 고민 시 참고했던 블로그
카드 이동 로직과 제일 많이 고민하고 논의했던 부분은 "DB 접근 횟수를 어떻게 줄일 수 있는가?"였다.
카드 CRUD 와 카드 이동이 발생할 때마다 DB에 접근하고,
또 모든 액션이 발생할 때마다 사용자 활동 기록을 보여주기 위해 로그를 DB에 쌓아야 했다.
로컬 스토리지에 데이터를 일정 시간 저장 후 서버에 요청하는 등 다양한 방식을 고려했으나
결론적으로는 write back
방식의 Redis
를 사용해서 데이터를 캐싱하기로 했다.
(JWT 토큰을 이용한 로그인 기능이 요구사항에 있어서, TTL을 설정해 Refresh token과 만료된 Access Token을 저장/처리하기 위함도 있었다.)
...라는 원대한 꿈을 꾸었지만 결론적으로 서버 배포 한번 해보고 Redis
관련 코드를 모두 들어냈다.
이유는 카드 이동 로직 관련 논의에 시간을 많이 쏟기도 했고,
JWT도 새로 공부해서 구현해야 했는데 Redis에 대한 이해도 부족과 공부할 시간 부족으로
낯선 에러를 처리할 수가 없었다... 🥲
나중에 참고차 에러 메시지를 적어두려 한다.
에러메시지 1
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
는 다음에 다시 시도해 보는 것으로!!
요번 프로젝트를 진행하면서 Trouble Shooting한 내용은 블로그에 별도로 정리해놓았다.
filter
기능 구현 후 CORS 설정이 먹히지 않다가 이런저런 설정을 하면서 해결이 되긴 했는데 원인을 정확히 모르겠다.
정보 감사합니다.