👩🏫 오늘의 출석
❓서른아홉 번째, 9 to 9을 해 본 소감❓
오늘은 팀 프로젝트 최종일로 팀 발표가 있었다.
📑오늘 학습한 내용
🧩오늘의 알고리즘 : 푸드 파이트 대회 🧩
문제 : 수웅이는 매달 주어진 음식을 빨리 먹는 푸드 파이트 대회를 개최합니다. 이 대회에서 선수들은 1대 1로 대결하며, 매 대결마다 음식의 종류와 양이 바뀝니다. 대결은 준비된 음식들을 일렬로 배치한 뒤, 한 선수는 제일 왼쪽에 있는 음식부터 오른쪽으로, 다른 선수는 제일 오른쪽에 있는 음식부터 왼쪽으로 순서대로 먹는 방식으로 진행됩니다. 중앙에는 물을 배치하고, 물을 먼저 먹는 선수가 승리하게 됩니다.
이때, 대회의 공정성을 위해 두 선수가 먹는 음식의 종류와 양이 같아야 하며, 음식을 먹는 순서도 같아야 합니다. 또한, 이번 대회부터는 칼로리가 낮은 음식을 먼저 먹을 수 있게 배치하여 선수들이 음식을 더 잘 먹을 수 있게 하려고 합니다. 이번 대회를 위해 수웅이는 음식을 주문했는데, 대회의 조건을 고려하지 않고 음식을 주문하여 몇 개의 음식은 대회에 사용하지 못하게 되었습니다.
예를 들어, 3가지의 음식이 준비되어 있으며, 칼로리가 적은 순서대로 1번 음식을 3개, 2번 음식을 4개, 3번 음식을 6개 준비했으며, 물을 편의상 0번 음식이라고 칭한다면, 두 선수는 1번 음식 1개, 2번 음식 2개, 3번 음식 3개씩을 먹게 되므로 음식의 배치는 "1223330333221"이 됩니다. 따라서 1번 음식 1개는 대회에 사용하지 못합니다.
수웅이가 준비한 음식의 양을 칼로리가 적은 순서대로 나타내는 정수 배열 food
가 주어졌을 때, 대회를 위한 음식의 배치를 나타내는 문자열을 return 하는 solution 함수를 완성해주세요.
제한사항
- 2 ≤
food
의 길이 ≤ 9
- 1 ≤
food
의 각 원소 ≤ 1,000
food
에는 칼로리가 적은 순서대로 음식의 양이 담겨 있습니다.
food[i]
는 i번 음식의 수입니다.
food[0]
은 수웅이가 준비한 물의 양이며, 항상 1입니다.
- 정답의 길이가 3 이상인 경우만 입력으로 주어집니다.
class Solution {
public String solution(int[] food) {
StringBuilder sb = new StringBuilder();
for (int i = 1; i < food.length; i++) {
int count = food[i] / 2;
sb.append(String.valueOf(i).repeat(count));
}
String answer = sb + "0";
answer += sb.reverse();
return answer;
}
}
🧩 오늘의 SQL : 없어진 기록 찾기 🧩
문제 : 천재지변으로 인해 일부 데이터가 유실되었습니다. 입양을 간 기록은 있는데, 보호소에 들어온 기록이 없는 동물의 ID와 이름을 ID 순으로 조회하는 SQL문을 작성해주세요.
SELECT O.ANIMAL_ID, O.NAME
FROM ANIMAL_INS I
RIGHT JOIN ANIMAL_OUTS O
USING (ANIMAL_ID)
WHERE I.ANIMAL_ID IS NULL
ORDER BY I.ANIMAL_ID
USING: JOIN 시 ON 할 필드의 이름이 같다면 USING(필드명)으로 ON A.필드명=B.필드명을 대신할 수 있다.
😎 프로젝트 회고 😎
🗂️ 프로젝트 설명
🐣 뉴스피드 프로젝트 : 너 내 동료가 돼라 - 프로젝트 팀원 구하는 사이트
뉴스 피드란? 블로그, 커뮤니티, SNS 등 내 게시물을 포함한 모든 게시물을 볼 수 있는 공간!
이번 프로젝트는 뉴스 피드를 포함해야하는 자유 주제 프로젝트였다.
필수 구현 기능과 공통 조건이 존재하였고 이후에는 추가 구현 기능 등이 있었지만 우리 조는 필수 기능 구현을 목표로 진행하였다.
파트는 기능 별로 인원에 맞게 5가지로 나누어 진행하였다.
❗ 필수 구현 기능 ❗
- 사용자 인증 기능
- 프로필 관리 기능
- 뉴스피드 게시물 CRUD 기능
❗ 공통 조건 ❗
- 예외처리는 아래와 같은 형태로 처리하여
Response
합니다.
Http Status Code | Message |
---|
400 | 잘못된 요청입니다. |
- Status Code 분류는 링크를 참고합니다.
- 모든 엔티티에는
생성일자
와 수정일자
가 존재합니다.
- 클라이언트는 Postman이고 프론트엔드는 별도 구현하지 않습니다.
- 추가 구현 단계에서 Swagger만 적용합니다.
그 외의 자세한 내용은 여기서 확인하기!
🛠️ 기술 스택
- Java 17
- IntelliJ
- Spring
- SpringBoot 3.3.0
- MySql
- aws ec2
🔊 협업 툴
Git, Github, Miro, Notion, Zep, Slack
🪄 구현 기능
- 사용자 인증 기능
-
Spring Security와 JWT를 사용하여 설계 및 구현
-
JWT는 Access Token, Refresh Token을 구현
-
Access Token 만료 시 : 유효한 Refresh Token을 통해 새로운 Access Token과 Refresh Token을 발급
-
Refresh Token 만료 시 : 재로그인을 통해 새로운 Access Token과 Refresh Token을 발급
-
API를 요청할 때는 Access Token을 사용함.
1-1-1. 회원가입
- 신규 가입자는 사용자 ID와 비밀번호를 입력하여 서비스에 가입함.
- ID 조건
- ID는 중복되거나 탈퇴한 ID로는 회원가입이 불가함.
- 또한 ID는 최소 10글자 이상, 최대 20글자 이상으로 작성해야함.
- 비밀번호 조건
- Bcrypt
로 단방향-인코딩 함.
- 대소문자 포함 영문 + 숫자 + 특수문자를 최소 1글자씩 포함해야 하며 최소 10글자 이상이어야 함.
1-1-2. 회원탈퇴
- 회원탈퇴는 가입된 사용자의 회원 상태를 변경하여 탈퇴처리함.
- 탈퇴 처리 시 비밀번호
를 확인한 후 일치할 때 탈퇴처리함.
- 조건
- 탈퇴한 사용자 ID는 재사용할 수 없고, 복구 또한 불가능 함.
- 탈퇴처리된 사용자는 재탈퇴 불가능함.
1-2-1. 로그인
- 사용자는 회원가입한 자신의 계정으로 서비스에 로그인 가능
- 조건
- 로그인 시 클라이언트에게 토큰을 발행해야 함.
- 로그인 시 header에 토큰을 추가하고 성공 상태의 메세지를 반환함.
- 탈퇴 또는 로그아웃 시 refresh Token이 유효하지 않은 상태가 되도록 함
1-2-2. 로그아웃
- 로그인 되어 있는 본인의 계정을 로그아웃 할 수 있음.
- 조건
- 로그아웃시 발행한 토큰은 초기화함.
- 로그아웃 후 초기화 된 refresh Token은 재사용 불가능 하며, 재로그인해야 함.
- 프로필 관리
2-1. 프로필 조회 기능
- 사용자 ID, 이름, 한줄 소개, 이메일 조회 가능.
- ID(사용자 ID X), 비밀번호, 생성일자, 수정일자와 같은 데이터는 노출하지 않음.
2-2. 프로필 수정
- 로그인한 상태의 사용자는 본인의 정보를 수정할 수 있음.
- 조건
- 비밀번호 수정 시에는 본인 확인을 위해 현재 비밀번호를 입력하여 올바른 경우에만 수정할 수 있도록 함.
- 뉴스피드 게시물 CRUD 기능
3-1. 게시물 작성, 조회, 수정, 삭제
- 게시물 조회는 모든 사용자가 가능 함
- 조건
- 게시물 작성, 수정, 삭제를 하기 위해서는 인가(Authorization)가 필요함.
- 유효한 JWT 토큰을 가진 작성자 본인만 처리할 수 있도록 함.
3-2. 뉴스피드 조회 기능
- 모든 사용자가 전체 뉴스피드 데이터를 조회할 수 있도록 함
- 조건
- 기본 정렬은 생성일자 기준으로 최신순으로 정렬함
- 뉴스피드가 없는 경우 Http Status Code - 200, Message - 먼저 작성하여 소식을 알려보세요! 로 반환함.
- 댓글 CRUD 기능
4-1. 댓글 작성, 조회, 수정, 삭제 기능
- 게시물에 댓글을 작성할 수 있으며, 수정 및 삭제는 본인만 가능함.
- 내용만 수정할 수 있으며 작성, 수정, 삭제 시 인가(Authorization)가 필요함.
- 유효한 JWT 토큰을 가진 작성자 본인만 처리할 수 있음.
🏹 트러블 슈팅
- 프로젝트 진행 과정
문제점 : 파트를 기능 별로 나누어 역할 분담을 하고 각자 파트 구현을 따로 구현을 해서 한 번에 합치려고 하니 기능이 제대로 동작하지 않거나 다른 파트가 구현이 되어야 진행할 수 있는 등의 문제가 발생함
해결 방법 : 각자 파트 구현 후 코드를 병합을 진행한 이후 부터는 필요한 코드를 추가 하거나 수정하며 틈틈히 수정한 부분에 대해 바로 머지하며 진행함
- Github 사용
문제점 : 최초 병합 시 PullRequest 머지를 사용하여 병합하였어야 했는데 그렇게 하지 않고 다른 팀원의 코드를 복사하여 붙여넣는 방식으로 진행하여 다른 팀원들은 병합중인 코드를 보기 어려웠고, 팀원의 코드가 누락되거나 시간이 오래 걸리는 문제가 발생함.
해결 방법 : develop 브랜치로 병합중인 코드를 공유하여 이후 부터는 함께 코드를 수정하며 진행함
- 기술적 어려움
문제점 : 토큰 관련 사용 및 구현에 대한 어려움
해결 방법 : 추후 토큰 관련한 추가적인 학습과 코드 구현 연습이 필요함
🤗 좋았던 부분
- GitHub Issue 기능을 사용한 점
1. 각 기능별로 이슈와 브랜치를 생성 후 개발 진행
2. 이후 develop 브랜치에서 1차 병합 진행
3. 최종 코드 마스터 브랜치에 publish
– 이 부분은 저번 팀 프로젝트 때 좋아서 이번 팀 프로젝트 때도 진행하면 좋을 것 같아서 진행해보았는데 역시나 이번에도 너무 좋았다. 팀원들과 불필요한 conflict를 막는 등의 이유에서 이슈를 생성하고 브랜치를 생성하여 진행한 점이 좋았다. 아마 앞으로도 팀프로젝트 시에는 계속 이 기능을 사용해서 이런 방식으로 진행할 것 같다.
- 공유 DB 사용한 점
– 로컬 DB가 아닌 팀원 모두가 함께 테스트를 진행할 수 있도록 공유 DB 사용한 점이 좋았다. 물론 각자 코드를 작성하는 동안은 로컬 DB를 사용했지만 병합을 진행할때부터는 공유 DB를 사용하여 새로운 방식으로 DB를 사용해 보았다는 점도 좋았고, 아무래도 여러명이 하나의 공통된 DB를 접근하기 위해 로컬 DB를 가지고 사용했다면 조금 번거로웠을 것 같은데 공유 DB를 사용하니 그럴 필요 없이 포트와 아이디, 비밀번호 하나를 공유하여 바로 사용가능 했다는 점이 좋았다. 다른 팀원들이 어떤 정보를 넣고, 어떤 식으로 언제 테스트를 했는지도 알 수 있고, 서로 하나의 데이터를 함께 보고, 사용 가능하다는 점이 아주 큰 장점이었다.
- 민감 데이터 환경변수화한 점
– application.properties 파일이 아닌 application.yml 파일을 사용하여 비밀번호, 아이디, 포트, SECRET 키 등 민감한 데이터를 외부에 노출시키지 않도록 환경변수화한 점이 아주 좋았다. 개인과제할 때 튜터님께 계속 피드백 받았던 부분이라 팀프로젝트 때도 적용하면 좋을 것 같아서 팀원들에게도 방법을 공유하고 환경변수화를 진행했는데 정말 잘한 일이라고 생각한다. 로컬 DB때도 GitHUb에 올릴 때 비밀번호 같은 부분이 꽤 신경 쓰였는데 이번 팀 프로젝트 때는 공유DB를 사용하다 보니까 더더욱 환경변수의 필요성을 느꼈다. 이게 과금이 될 수 있는 부분이다 보니까 민감한 데이터를 환경변수화 하여 우리 팀원들끼리만 중요 정보를 공유하고, GitHub에 푸시할 때 이런 부분을 따로 신경 쓸 필요가 없어 좋았다.
😣 아쉽다고 생각한 부분 & 🤔 개선하고 싶은 부분
- 최초 병합을 진행할 때 PullRequest를 통해 머지를 진행했다면 어땠을까?
– PullRequest를 통한 머지를 하는 방식을 잘 몰랐던 건지 팀원 한 분이 머지하지 않고 코드를 복사•붙여넣기를 통해 병합을 진행하여 특정 팀원의 코드가 누락되고, 다른 팀원들과 함께 코드를 공유하며 에러를 해결하지 못하는 일이 있었다. 최초 병합을 할 때는 원래 좀 에러가 많고, 이 부분에서 시간이 많이 걸리는데 이 부분을 팀원 전체가 함께 에러를 수정해나갔으면 조금 더 빨리 해결이 되었을 것 같은데 그렇게 할 수 없게 되어 이 부분에서 시간이 꽤 지체되었다고 생각한다. 물론 그 팀원 입장에서는 한 번에 병합을 진행하면 에러가 너무 많이 날 것 같다며 그런 식으로 진행하였던거지만 그럼에도 그런 방식으로 진행한 점에 대해서는 아쉬움이 남는다. 또한 진행 방식을 팀원들에게 먼저 공유하고 진행하였으면 좋았을텐데.. 그렇지 않았다는 점 또한 개선이 되어야할 부분이라고 생각된다.
- 코드 컨벤션과 초기 style guide 설정을 통해 코드의 일관성을 유지할 수 있었다면 어땠을까?
– 항상 팀프로젝트를 진행할 때 이 부분을 놓치고 시작하고선 팀 프로젝트가 끝날 때쯤 이 부분에 대해 생각하고, 미리 정하지 않은 것에 아쉬움을 느끼는 것 같다. 아직 팀 프로젝트를 진행하면서 한 번도 초기에 코드 컨벤션을 먼저 정하고 시작하지 않아서인지 매 번 팀 프로젝트 시작 시에 코드 컨벤션을 정하지 못하고 지나간다. 그렇다 보니 아무래도 코드 병합을 하고서 에러도 나고, 서로 수정이 필요한 부분도 생기며 시간을 사용하게 되다보니 이런 점이 아무래도 아쉬웠다. 다음 번 프로젝트부터는 꼭 코드 컨벤션을 먼저 진행하고 시작하도록 해야겠다.
- 추가 기능까지 구현할 수 있었다면 어땠을까?
– 위의 이런저런 여러 상황과 공휴일, 주말이 끼면서 생각한 것보다 조금 더 진행 속도가 늦어졌다. 물론 처음 목표는 필수 기능만이라도 제대로 완성하지는 것이었지만 그럼에도 시간이 조금 더 있었다면 추가 기능 중 우리가 구현할 수 있는 것들도 꽤 있어 보였는데 시도도 하지 못하고 마무리가 되어서 아쉬웠다. 또한 시간이 조금 촉박하다 보니 최종 자료 준비 부분에서 조금 미흡한 부분이 없진 않았던 것 같아 그 부분도 아쉬웠다.
💞 이번 팀 프로젝트를 통해 내가 배운 것
- 공유 DB 사용
- 공유 DB는 이번 팀 프로젝트를 진행하며 처음 사용해 보아서 정말 신기하고, 너무 좋았다. 이번 프로젝트 때는 다른 팀원이 만든 DB를 가져와 사용한 것이라 기회가 된다면 다음 번에 내가 직접 만들어서 어떻게 진행되는 건지 알고 싶다.
- 머지는 자주, 많이 할 수록 좋은 것!
- 이번 프로젝트 시에는 요일 하나를 정해서 그 날짜까지 각자 코드를 구현한 후 나머지 기간동안 병합을 진행하는 방식으로 계획을 짜고 프로젝트를 진행하였다. 그런데 그렇게 진행을 하다보니 최초 병합을 진행할 때 문제가 좀 많았다. 그리고 그 주에 튜터님께서 머지는 자주해줄 수록 좋은 것이라고 말씀해주셨다. 그 말을 듣고 보니 정말.. 그랬다. 그래서 우리도 그런식으로 프로젝트를 진행했어야하는데 하고 아쉬움과 후회가 되었다. 서로 연관된 부분이 많은 프로젝트라 각자 파트를 작성할 때 따로 구현할 수 있는 것이 아닌데 각자 구현을 하고 병합을 하다보니 너무 어렵고, 힘들고, 시간도 많이 걸렸다. 다음부터는 이런 일이 없도록 한 번에 병합 하지 말고 최대한 머지를 많이 진행하며 팀 프로젝트를 해야겠다고 생각했다.