Giter VIP home page Giter VIP logo

issue-tracker's Introduction





👋🏻 Team02

상추 짜왕 우디
Backend Backend Backend Frontend
피할 수 없으면 즐겨라 일요일은 내가 짜왕 요리사 같이 가! 가만 놔두면 다 해결 돼...



🏗️ 시스템 아키텍쳐

구조도



⚒️ TechStack

Front-End

  • HTML
  • CSS
  • JavaScript
  • React
  • Vite

Back-End

  • Java
  • SpringBoot
  • Spring Data JDBC
  • MySQL
  • Redis
  • Oauth2.0

Infra

  • GitHub Actions
  • Nginx
  • Docker
  • AWS
    • S3
    • EC2
    • Route53
    • CloudFront

Communication

  • GitHub
  • Slack
  • Notion



🛢️ ERD

erd



issue-tracker's People

Contributors

minjeongheo avatar zzawang avatar parksangchu avatar jihye-woo avatar crongro avatar godrm avatar

Stargazers

 avatar  avatar  avatar Kim Hyung Un avatar

issue-tracker's Issues

라벨 리스트, 개수

✏️ 기능 설명

라벨 목록과 라벨의 개수를 전송하는 API를 구현한다.



✅ TODO

  • 라벨 배경 색 랜덤 생성 URI 수정
  • 라벨 배경 색 DTO로 반환
  • 라벨 목록 API 구현
  • 라벨 개수 API 구현



📕 참고할만한 자료(선택)

레이블 수정, 삭제

✏️ 기능 설명

레이블을 수정, 삭제한다.



✅ TODO

  • 수정된 이름, 배경 색깔 공백 검사
  • 배경 색깔 유효성 검사
  • DB에 라벨의 수정된 값을 업데이트
  • DB에서 라벨 삭제
  • 구현한 기능 Test



📕 참고할만한 자료(선택)

https://spring.io/projects/spring-data-jdbc

라벨 글자 색, 카운팅

✏️ 기능 설명

  • 라벨 글자 색을 필드에 추가한다.
  • 라벨 리스트를 보내줄 때 라벨의 개수도 함께 전달한다.



✅ TODO

  • 라벨 글자 색 필드에 추가
  • 라벨의 총 개수 라벨 리스트 요청에 같이 담아 반환



이슈 필터링

✏️ 기능 설명

사용자가 요청한 필터 조건에 따라 필터링된 이슈 목록들을 반환한다.



✅ TODO

  • 필터 조건에 따른 동적 쿼리 생성
  • Repository마다 필요한 Read문 생성
  • 필터링된 이슈 리스트 반환
  • 열린/닫힌 이슈의 개수 반환



📕 참고할만한 자료(선택)

https://github.com/spring-projects/spring-data-relational/blob/main/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/mapping/AggregateReference.java

https://github.com/benelog/entity-dev/blob/master/src/index.adoc

회원 가입

✏️ 기능 설명

사용자 정보를 받아 새로운 회원을 생성한다.

✅ TODO

  • 사용자의 아이디가 중복되면 예외가 발생한다.
  • 생성된 회원을 DB에 저장하고 반환한다.

📕 참고할만한 자료(선택)

메인 이슈 리스트

✅ TODO

🚧 API 연동 전

✨레이아웃

  • Headr부분 레이아웃
    • 로고
    • 유저 이미지
  • Main 부분 레이아웃
  • 리스트 상단 필터링 드롭다운
    • 체크 박스
    • 열린 이슈 / 닫힌 이슈
    • 담당자 / 레이블 / 마일 스톤 / 작성자 필터 드롭다운
  • 상단 필터 input
  • 상단 레이블 버튼
  • 상단 마일 스톤 버튼
  • 상단 이슈 작성 버튼
  • 현재의 검색 필터 및 정렬 지우기 레이아웃
  • 리스트 - 라벨 개수 제한없는 레이아웃
  • 리스트 - 담당자 개수 제한없는 레이아웃
  • 리스트 - "검색과 일치하는 결과가 없습니다"

🛠 기능

  • 리스트 상단 필터링 드롭다운 체크 상태 관리(Reducer)
  • 필터 선택 시 input값에 value추가
  • 필터 선택 시 클리어(현재의 검색 필터 및 정렬 지우기) 기능
  • 전체 체크 박스 클릭
    • 리스트 체크 박스 전체 클릭 됨
    • nav 바뀜
    • 전체 체크 박스 개수 count
  • 전체 체크 박스 클릭 해지 시
    • 리스트 체크 박스 전체 클릭 해지 됨
    • nav 다시 바뀜
  • 리스트 체크 박스 클릭 시
    • nav 바뀜
    • 체크 박스 개수 count
  • 리스트 체크 박스 클릭 시
  • 리스트 검색 결과 없을 시 "검색과 일치하는 결과가 없습니다" 리스팅



✏️ 기능 설명



📕 참고할만한 자료(선택)

JWT로 로그인 기능 구현

✏️ 기능 설명

JWT를 이용하여 로그인 상태를 유지하고 검증할 수 있다.


✅ TODO

  • 로그인시 JWT를 발급할 수 있다.
  • 헤더에 담긴 JWT를 검증할 수 있다.
  • access 토큰을 재발급할 수 있다.
  • redis에 refresh 토큰을 저장하고 꺼낼 수 있다.


📕 참고할만한 자료(선택)

로그인

✅ TODO

  • 로그인 페이지 레이아웃 작업
  • useNavigate를 사용하여 페이지 링크
    • 회원가입 클릭 시 회원가입 페이지
    • 로그인 성공 시 인덱스 페이지
  • 기획서에 있는 기본적인 유효성 검사
    • 아이디 최소 6자리 ~ 최대 16자리 검사
    • 비밀번호 최소 6자리 ~ 최대 16자리 검사
  • 아이디, 비밀번호 미입력 시 로그인 버튼 비활성화
  • 아이디, 비밀번호 유효성 검사 넘어가면 로그인 버튼 활성화
  • 로그인 API 사용해서 로그인 처리
  • 로그인 현황(아이디, 닉네임) sessionStorage에 저장해놓기
  • input 태그 포커스 애니메이션
  • 타이핑 할 때마다 유효성 검사
  • 비밀번호 보이기(눈)
  • 비밀번호 *로 변환
  • input태그 입력할 때, 아닐 때 bg색상 구분하기
  • input 태그 잘못 입력했을 때 red
  • localStorage에 토큰 정보 저장

🛠 Refactoring



✏️ 기능 설명

js에서 localStorage.setItem()을 할 때, localStorage는 js의 오브젝트를 저장할 수 없다.
object -> string 바꿔 localstorage에 저장해야한다.
localstorage에 string으로 변환시켜주지 않고 setItem을 했을 경우



📕 참고할만한 자료(선택)

https://studyingych.tistory.com/28
https://sanghye.tistory.com/14

라벨 배경색 랜덤 생성

✏️ 기능 설명

라벨의 배경색을 랜덤으로 생성한다.



✅ TODO

  • 라벨 배경색 랜덤 생성
  • 구현한 기능 Test



📕 참고할만한 자료(선택)

이슈 열림/닫힘 여부에 따른 리스트 & 이슈 개수

✏️ 기능 설명

메인 화면에 열린 이슈의 목록과 닫힌 이슈의 목록이 보여지도록 구현한다. 또한 이슈의 개수를 각각 구하여 반환한다.



✅ TODO

  • 이슈의 열림/닫힘 여부에 따른 이슈 목록을 가져온다.
  • 열린 이슈 & 닫힌 이슈 각각의 개수를 담은 DTO를 전달한다.



📕 참고할만한 자료(선택)

Ec2 환경에서 DI 주입 문제 발생

🐞 버그 설명

로컬에서 스프링 프로젝트를 빌드할 때는 문제가 발생하지 않았는데, Ec2에서 프로젝트를 빌드하였더니 의존성 주입이 되지 않는 문제가 발생하였다.



🚨버그가 발생한 상황

  • 오류 메시지
org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with nameloginControllerdefined in URL [jar:nested:/home/ubuntu/issue-tracker/be/issue-tracker/build/libs/issuetracker-0.0.1-SNAPSHOT.jar/!BOOT-INF/classes/!/com/issuetracker/member/controller/LoginController.class]: Unsatisfied dependency expressed through constructor parameter 0: Error creating bean with nameloginServicedefined in URL [jar:nested:/home/ubuntu/issue-tracker/be/issue-tracker/build/libs/issuetracker-0.0.1-SNAPSHOT.jar/!BOOT-INF/classes/!/com/issuetracker/member/service/LoginService.class]: Unsatisfied dependency expressed through constructor parameter 0: Error creating bean with namememberRepositorydefined in com.issuetracker.member.repository.MemberRepository defined in @EnableJdbcRepositories declared on JdbcRepositoriesRegistrar.EnableJdbcRepositoriesConfiguration: Cannot resolve reference to beanjdbc.MemberRepository.fragments#0while setting bean propertyrepositoryFragmentsat org.springframework.beans.factory.support.ConstructorResolver.createArgumentArray(ConstructorResolver.java:795) ~[spring-beans-6.1.6.jar!/:6.1.6]
	at org.springframework.beans.factory.support.ConstructorResolver.autowireConstructor(ConstructorResolver.java:237) ~[spring-beans-6.1.6.jar!/:6.1.6]
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.autowireConstructor(AbstractAutowireCapableBeanFactory.java:1355) ~[spring-beans-6.1.6.jar!/:6.1.6]
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1192) ~[spring-beans-6.1.6.jar!/:6.1.6]
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:562) ~[spring-beans-6.1.6.jar!/:6.1.6]
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:522) ~[spring-beans-6.1.6.jar!/:6.1.6]
	at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:326) ~[spring-beans-6.1.6.jar!/:6.1.6]
	at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:234) ~[spring-beans-6.1.6.jar!/:6.1.6]
	at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:324) ~[spring-beans-6.1.6.jar!/:6.1.6]
.
.

위와 같이 loginController와 loginService 등등 Bean에 의존성 주입이 되지 않는 문제가 발생하였다.



😮 예상 동작 결과

정상적으로 빌드가 완료된다.



🤗 해결

jdk를 openJdk ➡️ Corretto로 변경하였더니 해결하였다. 찾아보니 빈 주입은 스프링 컨테이너가 하므로 jdk에 영향을 받지 않는다고 한다.

그러면 왜 해결된걸까?

  1. 정말로 jdk 문제였다.
  2. 처음에 실수로 application.properties를 없이 빌드했다.

React Query - POST 요청 후 refetch 이슈

🐞 버그 설명

useMutation을 이용하여 POST요청 후 useQueryClient.invalidateQueries를 이용한 refetch 요청 불가



🚨버그가 발생한 상황

POST요청은 네트워크 탭에서 200코드를 확인하여 성공적으로 이루어지는 걸 확인,
refetch하기 위해서 useQueryClient.invalidateQueries를 실행하려고 했지만 실패

... 작성중



😮 예상 동작 결과

POST요청이 성공(200)하였기 때문에 GET요청을 refetch하여 화면의 상태가 바뀔 것이라고 생각했습니다.



🤗 해결

  1. useMutation의 mutationFn 콜백 함수는 비동기 함수이므로 async await키워드를 붙여줬습니다.
const { mutate: toggleIssueState } = useMutation({
  mutationFn: async ({ toIssueState, issueIds }) => {
      return await fetchIssueStateToggle(toIssueState, issueIds);
  },
  ...
  1. POST요청 후 respponse.json()을 호출하여 에러가 발생했습니다.
    응답에 body가 없는 경우 response.json()을 호출하면 에러가 발생합니다.
    응답에 JSON 데이터가 없을 경우에는 .json()을 호출하지 않고, 대신 상태 코드만 확인하거나 필요한 다른 정보를 반환할 수 있습니다.
export const fetchIssueStateToggle = async (toIssueState, issueIds) => {
    try {
        const response = await fetch(`${import.meta.env.VITE_TEAM_SERVER}${ISSUE_DEFAULT_API_URI}${toIssueState ? 'close' : 'open'}`, {
            method: 'POST',
            headers: {
                'Content-Type': 'application/json',
            },
            body: JSON.stringify({ issueIds }),
        });

        if (!response.ok) throw new Error(`HTTP error! status: ${response.status}`);

        const result = await response.json(); // 이 부분에서 Error

        return result;
    } catch (error) {
        throw error;
    }
};

🔽

export const fetchIssueStateToggle = async (toIssueState, issueIds) => {
    try {
 ...
        if (!response.ok) throw new Error(`HTTP error! status: ${response.status}`);

        return {
              status: response.status,
              statusText: response.statusText,
          };
    } catch (error) {
        throw error;
    }
};
  1. 일단 2번에서 정상적으로 실행이 안됐기 때문에 useMutation의 onSuccess콜백이 호출되지 않았습니다.
    onSuccess 콜백이 호출되지 않는 문제는 useMutation의 mutationFn(2번 로직)이 제대로 Promise를 반환하지 않거나, 반환된 Promise가 올바르게 처리되지 않았기 때문이었습니다.

2번 오류를 처리하였지만 그래도queryClient.invalidateQueries 가 실행되지 않아, refetch가 여전히 되지 않았습니다.

  1. 쿼리 키의 일관성은 매우 중요합니다. 중요한 점은 타입까지 동일하게 해야 했습니다.
    ['issueDetail', '1']['issueDetail', 1]은 서로 다른 쿼리 키로 인식됩니다.
    refetch하려는 쿼리의 key는['issueDetail', '1'] 이었는데,
    invalidateQueries에서 실행 시키려던 쿼리의 키를 ['issueDetail', 1] 로 설정하였기 때문에 동일한 키라고 인식하지 못하여 refetch가 되지 않았던 것 입니다!
    string으로 타입을 일치시켜 해결하였습니다.

타입스크립트를 사용해야하는 이유를 확실히 느꼈습니다.....

queryClient.invalidateQueries({ queryKey: ['issueDetail', String(id)] })

invalidateQueries를 사용해서 키값에 해당하는 데이터를 invalidate(무효화) 시킨다.
그러면 해당 데이터가 즉각 stale 상태(데이터가 만료된 상태)가 되어 refetching 된다.

정리하자면:
post 호출
→ 성공시(onSuccess) ["키"] 값에 해당하는 데이터 날려버림
→ 데이터 refetch

이슈 상세

✅ TODO

  • 레이아웃


✏️ 기능 설명



📕 참고할만한 자료(선택)

메인 이슈 필터링

✏️ 기능 설명

이슈 리스트 필터 선택



✅ TODO

필터 선택 안할 시

  • 화면 input값 형식 milestone:"no"no:milestone
  • API 요청 시 params에 ?noValues=값;값
    key는 noValues value는 ; 으로 구분지어서 요청



📕 참고할만한 자료(선택)

예외처리 중복 제거

✏️ 기능 설명

예외 처리시 중복되는 로직을 CustomException을 이용해 제거한다.


✅ TODO

  • CustomException 클래스 추가하여 상속
  • 공용 예외처리 어드바이스에 HandleCustomException 메서드 추가



📕 참고할만한 자료(선택)

코드 리팩토링

✏️ 기능 설명

전반적인 구조와 코드를 리팩토링한다.



✅ TODO

  • 접근 수준을 갖는 생성자로 롬복 수정하기 (Ex) @allargsconstructor(access = accesslevel.private))
  • immutable한 값을 반환하도록 변경하기
  • LoginService 코드 분리
  • 필터링 n+1쿼리 문제 해결
    • ListCrudRepository 사용해보기
  • 이슈 상태관리 n+1쿼리 문제 해결
  • @ControllerAdvice ➡️ @RestControllerAdvice
  • 라벨 개수와 마일스톤 개수를 묶어서 보내는 API 구현
  • 필터링 API의 열린/닫힌 이슈 개수 Count 수정



📕 참고할만한 자료(선택)

Ec2환경에서 Redis 연결이 되지 않는 문제 발생

🐞 버그 설명

로컬 환경에서는 redis를 설치한 후 실행이 정상적으로 작동이 되었는데, Github Actions를 통해 빌드하고 도커 이미지를 만들어서 배포하는 과정에서 연결이 안되는 문제가 발생하였다.



🚨버그가 발생한 상황

  • 오류 메시지
io.netty.channel.AbstractChannel$AnnotatedConnectException: Connection refused: localhost/127.0.0.1:6379
Caused by: java.net.ConnectException: Connection refused
	at java.base/sun.nio.ch.Net.pollConnect(Native Method) ~[na:na]
	at java.base/sun.nio.ch.Net.pollConnectNow(Net.java:672) ~[na:na]
	at java.base/sun.nio.ch.SocketChannelImpl.finishConnect(SocketChannelImpl.java:946) ~[na:na]
	at io.netty.channel.socket.nio.NioSocketChannel.doFinishConnect(NioSocketChannel.java:337) ~[netty-transport-4.1.109.Final.jar!/:4.1.109.Final]
	at io.netty.channel.nio.AbstractNioChannel$AbstractNioUnsafe.finishConnect(AbstractNioChannel.java:339) ~[netty-transport-4.1.109.Final.jar!/:4.1.109.Final]
	at io.netty.channel.nio.NioEventLoop.processSelectedKey(NioEventLoop.java:776) ~[netty-transport-4.1.109.Final.jar!/:4.1.109.Final]
	at io.netty.channel.nio.NioEventLoop.processSelectedKeysOptimized(NioEventLoop.java:724) ~[netty-transport-4.1.109.Final.jar!/:4.1.109.Final]
	at io.netty.channel.nio.NioEventLoop.processSelectedKeys(NioEventLoop.java:650) ~[netty-transport-4.1.109.Final.jar!/:4.1.109.Final]
	at io.netty.channel.nio.NioEventLoop.run(NioEventLoop.java:562) ~[netty-transport-4.1.109.Final.jar!/:4.1.109.Final]
	at io.netty.util.concurrent.SingleThreadEventExecutor$4.run(SingleThreadEventExecutor.java:997) ~[netty-common-4.1.109.Final.jar!/:4.1.109.Final]
	at io.netty.util.internal.ThreadExecutorMap$2.run(ThreadExecutorMap.java:74) ~[netty-common-4.1.109.Final.jar!/:4.1.109.Final]
	at io.netty.util.concurrent.FastThreadLocalRunnable.run(FastThreadLocalRunnable.java:30) ~[netty-common-4.1.109.Final.jar!/:4.1.109.Final]
	at java.base/java.lang.Thread.run(Thread.java:840) ~[na:na]



🤗 해결

Docker Compose(여러 컨테이너로 구성된 애플리케이션을 정의하고 실행하기 위한 도구)를 사용해서 redis container를 종속하는 spring server를 실행하여 해결하였다.

무한 렌더 이슈

🐞 버그 설명

무한 렌더 발생

Image
Warning: Maximum update depth exceeded. This can happen when a component calls setState inside useEffect, but useEffect either doesn't have a dependency array, or one of the dependencies changes on every render.



🚨버그가 발생한 상황

  1. Given:
    • React Query(useQueries)를 사용하여 데이터를 병렬로 가져오고, useFiltersData 훅을 통해 filterResults 배열로 반환합니다.
    • filterResults 배열은 여러 쿼리 상태를 포함합니다.
  import { useFiltersData } from '../../hooks/useFiltersData';
  const filterResults = useFiltersData();
  const [labelsResult, membersResult, milestonesOpenResult, milestonesClosedResult] = filterResults;
  1. When:
    • useEffect 훅의 종속성 배열에 filterResults를 설정합니다.
useEffect(() => {
    if (filterResults.some((result) => !result.data)) return;

    const milestoneOpenItems = milestonesOpenResult.data.milestoneDetailDtos.map(({ name }) => ({
        title: name,
    }));
    ... 생략

    setFilterItemsByType((prev) => ({ ...prev, milestones: [...milestoneOpenItems, ...milestoneClosedItems] }));
}, [filterResults]);
  1. Then:
    • filterResults 배열 자체가 매 렌더링마다 새로운 참조로 생성되기 때문에 useEffect 훅이 무한 루프에 빠집니다.



😮 예상 동작 결과

각 fetch요청이 완료 될 때만 리렌더링이 될 것이라고 예상했습니다.



🤗 해결

useEffect의 종속성 배열에 filterResults를 설정했을 때 무한 루프에 빠지는 이유는, filterResults 배열 자체가 매 렌더링마다 새로운 참조로 생성되기 때문입니다.
React Query의 useQueries 훅은 쿼리 상태를 반환하는 배열을 반환하지만, 이 배열이 매번 새로운 참조로 생성되기 때문에 useEffect가 지속적으로 실행되었던 것 입니다.

  • 종속성 배열에 filterResults 배열의 개별 쿼리 결과(data)로 변경시킵니다.
  • 이렇게 하면 개별 쿼리 결과가 변경될 때만 useEffect가 실행됩니다.
useEffect(() => {
        if (filterResults.some((result) => !result.data)) return;

        const milestoneOpenItems = milestonesOpenResult.data.milestoneDetailDtos.map(({ name }) => ({
            title: name,
        }));
        const milestoneClosedItems = milestonesClosedResult.data.milestoneDetailDtos.map(({ name }) => ({
            title: name,
        }));

        setFilterItemsByType((prev) => ({ ...prev, milestones: [...milestoneOpenItems, ...milestoneClosedItems] }));
    }, [filterResults]);

🔽

useEffect(() => {
        if (filterResults.some((result) => !result.data)) return;

        const milestoneOpenItems = milestonesOpenResult.data.milestoneDetailDtos.map(({ name }) => ({
            title: name,
        }));
        const milestoneClosedItems = milestonesClosedResult.data.milestoneDetailDtos.map(({ name }) => ({
            title: name,
        }));

        setFilterItemsByType((prev) => ({ ...prev, milestones: [...milestoneOpenItems, ...milestoneClosedItems] }));
    }, [labelsResult.data, membersResult.data, milestonesOpenResult.data, milestonesClosedResult.data]);

CI/CD 구축

✏️ 기능 설명

프로젝트를 자동 배포하기 위해 CI/CD 파이프라인을 구축한다.



✅ TODO

  • 테스트 실행하는 CI 구축
  • Github Actions를 사용하여 Jar파일 빌드
  • dockerfile을 통해 이미지를 빌드 & docker repo로 push
  • Ec2 Ubuntu 서버에 접속 후 Docker 이미지 가져와서 배포



📕 참고할만한 자료(선택)

https://docs.github.com/en/actions/creating-actions/creating-a-docker-container-action
https://docs.docker.com/build/ci/github-actions/

이슈 필터링 구조 변경

✏️ 기능 설명

이슈 필터링 조건을 받는 쿼리 파라미터들을 변경함에 따라 구조를 변경한다.



✅ TODO

  • 필터링 DTO 라벨 아이디 -> 이름으로 변경
  • 필터링 DTO 마일스톤 아이디 -> 이름으로 변경
  • 필터링 DTO에 작성자, 라벨, 마일스톤이 없는 조건을 체크할 필드 추가
  • 담당자 Id 리스트 -> SimpleMemberDto(아이디 & 파일 url)리스트로 변경(Fileservice의 getImgUrlById() 사용)



로그인

✏️ 기능 설명

아이디 및 패스워드로 로그인을 할 수 있다.

✅ TODO

  • 아이디 및 패스워드가 일치하지 않으면 예외를 발생시킨다.
  • 일치한다면 로그인 유저 객체를 반환한다.

📕 참고할만한 자료(선택)

환경 설정 및 초기 셋팅

✅ TODO

  • Vite 프로젝트 생성
  • Vite 포트 번호 설정
  • Styled Component 설치

✏️ 기능 설명

⚓Git Brach Team Convention

fe/init 브랜치 생성하기

master 브랜치로 이동:

git checkout master

master 브랜치의 최신 상태를 가져옵니다 (원격 저장소에서):

git pull

새로운 branch를 생성하고 해당 branch로 이동합니다

git checkout -b *fe/init*

이제 new-branch에 master 브랜치의 모든 데이터가 복사되었습니다. 필요에 따라 원격 저장소에 새로운 branch를 푸시할 수 있습니다.

새로운 branch를 원격 저장소에 푸시합니다:

git push -u origin *fe/init*

Vite 프로젝트 생성

Vite guide

vite 로 프로젝트를 생성 시, 상위 폴더 생성없이 해당 디렉토리 (FE 디렉토리)에 바로 셋팅하려면

$ npm create vite@latest

이 명령어로 실행하지 않고, 아래 명령어로 실행합니다.

$ npm init vite .

📦issue-tracker
 ┣ 📂.git
 ┣ 📂**FE**
 ┃ ┣ 📂public
 ┃ ┃ ┗ 📜vite.svg
 ┃ ┣ 📂src
 ┃ ┃ ┣ 📂assets
 ┃ ┃ ┃ ┗ 📜react.svg
 ┃ ┃ ┣ 📜App.css
 ┃ ┃ ┣ 📜App.jsx
 ┃ ┃ ┣ 📜index.css
 ┃ ┃ ┗ 📜main.jsx
 ┃ ┣ 📜.eslintrc.cjs
 ┃ ┣ 📜.gitignore
 ┃ ┣ 📜index.html
 ┃ ┣ 📜package.json
 ┃ ┣ 📜README.md
 ┃ ┗ 📜vite.config.js
 ┗ 📜README.md

$ npm install

$ npm run dev


Port setting

Vite local port 는 기본적으로 3000 이 아닌, 5173 이므로

vite.config.js 파일에서 포트 번호를 설정해줄 수 있습니다.

server: {
        port: 3000,
    },



Port 종료시키기

Windows에서 포트 사용 중인 프로세스 찾기 및 종료하기
  1. 명령 프롬프트에서 사용 중인 포트 찾기 :

    • cmd를 열고 다음 명령어를 입력합니다.
    netstat -ano | findstr :YOUR_PORT_NUMBER
    
    • 여기서 YOUR_PORT_NUMBER는 확인하고자 하는 포트 번호입니다. 예를 들어, 포트 3000이 사용 중인지 확인하려면:
    netstat -ano | findstr :3000
    
  2. 프로세스 종료하기:

    • netstat 명령의 결과에서 마지막 열에 있는 PID(프로세스 ID)를 확인합니다.
    • Task Manager를 열거나 다음 명령어로 해당 PID의 프로세스를 종료합니다.
    taskkill /PID YOUR_PID_NUMBER /F
    
    • 예를 들어 PID가 1234인 프로세스를 종료하려면:
    taskkill /PID 1234 /F
    

macOS와 Linux에서 포트 사용 중인 프로세스 찾기 및 종료하기

  1. 터미널에서 사용 중인 포트 찾기:

    • 터미널을 열고 다음 명령어를 입력합니다.
    sudo lsof -i :YOUR_PORT_NUMBER
    
    • 예를 들어, 포트 3000이 사용 중인지 확인하려면:
    sudo lsof -i :3000
    
  2. 프로세스 종료하기:

    • lsof 명령의 결과에서 PID를 확인합니다.
    • 다음 명령어로 해당 PID의 프로세스를 강제 종료합니다.
    kill -9 YOUR_PID_NUMBER
    
    • 예를 들어 PID가 5678인 프로세스를 종료하려면:
    kill -9 5678
    

이러한 단계를 통해 포트를 사용 중인 프로세스를 찾아 종료할 수 있습니다.

명령어 사용에 주의해야 하며, 필요한 경우 관리자 권한으로 실행해야 할 수도 있습니다.


📕 참고할만한 자료

https://ko.vitejs.dev/guide/
https://velog.io/@wynter24/Vite-local-port-변경

라벨 리팩토링

✏️ 기능 설명

Sprint1에서 BE 리뷰어로부터 리뷰받은 사항을 적용한다.



✅ TODO

  • 메소드 이름으로 의미가 잘 들어난다면 굳이 주석쓰지 않기
  • 예외가 왜 발생했는지 로깅하기
  • 호출 될 때마다 Random 인스턴스를 생성하는 부분 수정하기
  • @transactional 적용
  • DTO setter 없애기
  • getMember ➡️ toMemebr or toMemebrDto로 변경



📕 참고할만한 자료(선택)

주석 미작성된 서비스에 주석 작성

✏️ 기능 설명

주석이 미작성된 서비스에 javadoc 형태로 주석을 작성한다.


✅ TODO

  • 주석이 미작성된 서비스의 public 메서드에 javadoc 형태로 주석을 작성한다.



📕 참고할만한 자료(선택)

메인 README 작성

✏️ 기능 설명

릴리즈용 브랜치인 메인에 보여질 리드미를 작성한다. 우선, main-docs 브랜치를 새로 생성하여 작업한다.



✅ TODO

  • 팀 소개
  • 그라운드 룰
  • 문서(피그마, 팀 노션) 안내
  • ERD
  • 사용 기술 스택



마일스톤 관리

✏️ 기능 설명

마일스톤을 관리하는 기능을 추가한다.

✅ TODO

  • 마일스톤을 생성한다.
  • 마일스톤을 수정한다.
  • 마일스톤을 삭제한다.
  • 마일스톤을 열고 닫는다.
  • 열림/닫힘 여부에 따라 마일스톤 리스트를 조회한다.
  • 개별 마일스톤을 조회한다.

📕 참고할만한 자료(선택)

컨트롤러 테스트 추가

✏️ 기능 설명

컨트롤러에 실패할 경우의 테스트도 추가한다.



✅ TODO

라벨 생성 API

  • 400 테스트
  • 409 테스트
  • location 헤더 설정이 되었는지 확인하는 테스트 추가

라벨 수정 API

  • 400 테스트
  • 404 테스트

라벨 삭제 API

  • 404 테스트



📕 참고할만한 자료(선택)

코멘트 CRUD

✏️ 기능 설명

코멘트를 생성, 수정, 삭제할 수 있다.


✅ TODO

  • 코멘트를 생성할 수 있다.
  • 코멘트의 본문을 수정할 수 있다.
  • 코멘트를 삭제할 수 있다.



📕 참고할만한 자료(선택)

이슈 생성, 수정, 삭제

✏️ 기능 설명

이슈 관리와 관련된 기능등을 구현한다.

✅ TODO

  • 이슈를 생성할 수 있다.
  • 이슈를 수정할 수 있다.
  • 이슈를 열고 닫을 수 있다.
  • 이슈를 삭제할 수 있다.



📕 참고할만한 자료(선택)

라우터 지정하기

✅ TODO

  • react-router-dom 을 사용하여 라우터 지정하기 (인덱스, 회원가입, 로그인)
  • 잘못된 경로의 라우터 지정하기



✏️ 기능 설명

React Router Tutorial

$ npm install react-router-dom

📦src
┣ 📂components
┃ ┣ 📜Header.jsx
┃ ┣ 📜Index.jsx
┃ ┣ 📜Join.jsx
┃ ┣ 📜Login.jsx
┃ ┣ 📜Main.jsx
┃ ┗ 📜NotFound.jsx
┣ 📂router
┃ ┣ 📜ProtectedRoute.jsx
┃ ┗ 📜routes.jsx

┣ 📜App.jsx
┗ 📜main.jsx

App.jsx에서 routes.jsx 컴포넌트로 분리하고 AppRoutes(routes.jsx)에서 라우팅 작업을 했습니다.

여러 환경에서 동작할 수 있도록 여러 종류의 라우터 컴포넌트를 제공합니다.
이중 가장 많이 사용하는 라우터 컴포넌트는 BrowserRouter와 HashRouter입니다.

<BrowserRouter>, <Routes>, <Route>, <Link> 컴포넌트 사용하기

import { BrowserRouter as Router, Route, Routes, Link  } from 'react-router-dom';
  • <Routes>
    여러 Route를 감싸서 그 중 규칙이 일치하는 라우트 단 하나만을 렌더링 시켜주는 역할을 합니다.  

  • <Route>
    path속성에 경로, element속성에는 컴포넌트를 넣어 줍니다.

  • <Link>
     - 웹 페이지에서는 원래 링크를 보여줄 때 a태그를 사용합니다. 
      하지만 a태그는 클릭 시 페이지를 새로 불러오기 때문에 사용하지 않는다.
      생김새는 a태그를 사용하지만, History API를 통해 브라우저 주소의 경로만 바꾸는 기능이 내장되어 있습니다.
    <Link to="경로">링크명</Link>
     

사전에 정의하지 않은 경로로 접근하는 경우 NotFound 페이지로 이동 처리

<Route path="*" element={<NotFound />} />



📕 참고할만한 자료(선택)

https://reactrouter.com/en/main/start/tutorial
https://goddaehee.tistory.com/305
https://danminblog.tistory.com/69

레이블 편집

✏️ 기능 설명



✅ TODO

  • [ ]
  • [ ]



📕 참고할만한 자료(선택)

팀 노션 생성 및 필요한 Docs 작성

✏️ 기능 설명

팀 협업을 위한 워크스페이스를 위해 노션을 생성하였다. 회의록 및 필요한 Docs를 관리할 예정이다.



✅ TODO

  • 팀 소개 생성
  • 회의록 페이지 생성
  • 필요한 Docs 페이지 (그라운드 룰, Conventions, 템플릿, API Docs, ERD, 버그 제보 등등)생성



📕 참고할만한 자료(선택)

파일 저장 및 조회

✏️ 기능 설명

이슈 및 코멘트 저장, 조회에서 사용할 파일 서비스를 S3와 연동하여 구현한다.

✅ TODO

  • 프로젝트와 S3를 연동할 수 있다.
  • 파일을 S3 및 데이터베이스에 저장할 수 있다.
  • 파일을 S3 및 데이터베이스에서 가져올 수 있다.
  • 파일을 S3 및 데이터베이스에서 삭제할 수 있다.

📕 참고할만한 자료(선택)

이슈 상세

✏️ 기능 설명

이슈 상세 내용을 조회할 수 있다.


✅ TODO

  • 이슈의 본문을 가져온다.
  • 이슈에 첨부된 파일을 가져온다.
  • 이슈의 담당자, 라벨, 마일스톤을 가져온다.
  • 이슈의 코멘트를 가져온다.



📕 참고할만한 자료(선택)

멤버 및 글로벌 패키지 리팩토링

✏️ 기능 설명

코드 리뷰를 바탕으로 멤버 및 글로벌 패키지의 코드를 리팩토링한다.

✅ TODO

  • [] 멤버 패키지 리팩토링
  • [] 글로벌 패키지 리팩토링

📕 참고할만한 자료(선택)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.