더 이상 필요 없는 굿즈들을 실시간 경매를 통해 구매, 판매 할 수 있는 서비스 플랫폼
📅 프로젝트 기간 : 2024.07.19 ~ 2024.08.16
🧸 판매하고 싶은 상품을 경매에 등록해보세요!
💸 실시간으로 경매에 참여하여 원하는 물건을 구매해보세요!
⌨️ 경매 참여한 유저들과 채팅으로 소통해보아요!
GoodsEnding은 더 이상 필요 없어진 굿즈를 실시간 경매를 통해 구매하고 판매할 수 있는 플랫폼입니다. 사용자는 간편하게 상품을 등록하고, 다른 사용자와 실시간 채팅으로 소통하며 경매에 참여할 수 있습니다.
🔗 시연 영상
- 레포지토리 클론
git clone <https://github.com/goods-ending/goodsending-fe.git>cd <project-directory>
- Node 설치 확인
node -v
- Yarn 설치 확인
yarn -v
- 의존성 설치
yarn install
.env작성
VITE_API_URL="http://15.164.70.82:8080/"
- React App 실행
yarn start
👥 [사용자] 회원 가입
- 1️⃣ SecureRandom.getInstanceStrong() 난수 생성 메서드
- 2️⃣ 네이버 SMTP (이메일을 전송할 때 사용되는 표준 통신 프로토콜)
- 3️⃣ Redis
- 4️⃣ 사용자 정의 애너테이션
- 생성되는 숫자는 6자리 밖에 안되지만 강력한 보안성을 위해 사용
- 한국 사용자에게 최적화된 SMTP 서버 이며, 국내 환경에 맞는 보안 설정과 사용성이 장점인 네이버 SMTP를 선택
- 인증코드는 단순하게 확인만 하면 되는 정보이므로 인메모리 데이터 저장 구조인 Redis를 사용해서 빠르게 읽을 수 있고, 자동으로 만료되는 TTL 기능도 추가하여 메모리 사용을 효율적으로 관리할 수 있도록 적용
- 사용자 정의 애너테이션을 만들어 정규 표현식 기반으로 비밀번호를 검증하고, 암호화 하여 DB에 저장되도록 설정
🔐 [사용자] 로그인 / 로그아웃
- 1️⃣ 사용자 정보를 통한 JWT 토큰을 발급하는 방식으로 구현
- 2️⃣ JWT 인증 필터를 이용하여 자동적으로 토큰의 유효성 검사를 하도록 설정
- 3️⃣ JWT Access Token을 생성할 때 Refresh Token을 같이 생성해 Redis에 저장하고, API를 호출하기 전에 토큰이 만료되었는지 검사 후 만료되었으면 Redis에 저장된 Refresh Token을 확인 해 유효할 시 Access Token을 재발급하는 방법으로 사용하여 보안성을 강화하면서도 인증을 다시 하지 않아도 되도록 편의성을 갖추도록 구현
- 4️⃣ 로그아웃 경우 쿠키와 Redis에 저장되어 있는 Refresh Token은 삭제되고, Access Token은 남은 만료 시간 만큼 Redis에 저장되어 재사용이 불가능 하도록 구현
🪪 [사용자] 마이페이지
💰 캐시 및 포인트
🏷️ [상품] 상품 판매 등록
📋 [주문] 낙찰된 주문 관리
✨ [주문] 낙찰자 선정 및 주문 진행
📊 [인기 순위] 경매 상품 인기 순위
💬 [채팅] 실시간 경매 상품 채팅
❤️ [경매 상품] 찜
🔍 [경매 상품] 검색 및 필터링 기능
- 1️⃣ QueryDSL을 사용한 검색 기능
- 2️⃣ 커서 기반 페이지네이션
- 3️⃣ 경매 진행 상품, 경매 진행 될 상품, 경매 종료 된 상품 순으로 사용자의 접근성을 고려하여 정렬
- 4️⃣ 키워드, 경매 진행 상품 & 경매 진행 될 상품, 경매 종료 된 상품을 조건으로 원하는 상품에 빠르게 접근할 수 있도록 검색 결과를 제공
- 무분별한 상품 등록을 막고자 보증금 서비스 도입
- 상품을 등록시 판매자의 캐시에서 보증금 지불
- 보증금 금액: 경매 최소 가격의 5%(최소 3000원)
- 무분별한 상품 등록을 막고자 보증금 서비스 도입
- 확정금액(=낙찰자의 입찰가)의 2.5%의 포인트가 낙찰자 포인트에 적립
MySQL Ngram을 적용하여 검색 성능을 향상하거나, 대용량 데이터 처리가 필요할 경우를 대비하여 Elastic Search를 도입하여 대용량 인덱스를 관리하고 싶습니다.
Yarn- Yarn은 패키지 관리 도구로, 패키지를 설치할 때 캐시를 사용하여 이전에 다운로드한 패키지를 재사용하므로 설치 속도가 빠르고, 대규모 프로젝트에 효율적입니다.yarn.lock파일을 생성하여 의존성의 정확한 버전을 기록하기에 팀원 간의 환경 차이를 줄이고, 일관된 빌드를 보장하며 오류 발생 시 명확하고 구체적인 메시지를 제공하여 문제 해결을 용이하게 해준다는 장점이 있어 패키지 관리 도구로 Yarn을 사용했습니다.Tailwind CSS- Tailwind CSS는 미리 정의된 유틸리티 클래스를 사용하여 빠르게 스타일을 적용할 수 있기에 스타일링에 소요되는 시간을 크레 줄일 수 있다는 장점이 있습니다. 설정 파일을 통해 쉽게 커스터마이즈 할 수 있고 모든 스타일이 클래스 형태로 정의되어 팀원간의 스타일링 차이를 최소화할 수 있다는 장점이 있습니다.Shadcn UI- Shadcn UI는 Tailwind CSS 기반의 UI 컴포넌트 라이브러리로, 일관성 있는 UI 개발에 유리 Tailwind UI는 Tailwind CSS 기반의 UI 킷이지만, 컴포넌트 구현이 필요하다. 개발 생산성을 고려했을 때 Shadcn UI가 더 적합할 것 같았기에 Shadcn UI 결정Axios- Fetch는 기본적인 기능이 필요할 때는 충분히 사용할 수 있지만, 에러 처리와 추가 기능 구현에 번거로움이 있을 수 있습니다. Fetch와 비교하여 에러 객체를 통해 상세한 응답 정보를 제공할 수 있어 에러 처리에 용이 합니다. 요청과 응답을 가로채는Axios interceptor기능을 제공하여 토큰 재발급 및 요청 수정 등을 쉽게 처리할 수 있습니다. 또한 JSON 데이터를 자동으로 변환해주고, 파일 업로드를 위한 설정이 간편하며 다양한 브라우저에서 잘 작동한다는 브라우저 호환성이 뛰어나다는 점에 Axios를 사용했습니다.Redux- Redux는 중앙 집중식 상태 관리를 통해 예측 가능한 상태 변화를 제공하며, 단일 스토어로 일관성을 유지합니다. 불변성을 통해 상태 추적이 용이하고, 미들웨어와 개발 도구를 통해 비동기 작업 및 디버깅을 간편하게 지원합니다.
- Language : JavaScript (ES6)
- Framework : React
- State Management : Redux
- Build Tool : AWS Amplify
- Package Manager : yarn
- UI Library : Tailwind CSS
- HTTP Client : Axios
- Formatting : Prettier
- Version Control : Git
- Deploy
- AWS EC2
- Docker
- Github Actions
- AWS ECR
- Communication
- Slack
- Github
- Notion
🧨 웹소켓 채팅 기능에서 한글 입력 후 엔터 시 두 번 전송되는 문제
웹소켓을 이용한 채팅 기능에서, 한글 입력 후 onkeydown 이벤트로 엔터를 입력할 때 동일한 메시지가 두 번 전송되는 문제가 발생
이로 인해 채팅 메시지가 중복 전송되어 사용자 경험에 부정적인 영향을 미쳤음
- 원인
- 해결 방법
- 한글 입력 시 IME(입력기)가 활성화되며, 글자가 조합 중일 때와 조합이 완료된 후에 각각 이벤트가 발생하는 경우가 있음
- onkeydown 이벤트가 두 번 발생해 동일한 메시지가 중복으로 전송되는 현상이 발생
- 과거에는 이 문제를 해결하기 위해 onKeyPress 이벤트를 사용할 수 있었지만, keypress 이벤트는 더 이상 권장되지 않음. MDN 문서에서도 keypress 이벤트가 표준에서 제외될 가능성이 있음을 경고하고 있음
- onkeydown 이벤트를 그대로 사용하면서, IME 입력 중인지 여부를 감지하기 위해 KeyboardEvent.isComposing 속성을 활용
isComposing 속성은 조합 문자를 작성할 때 true를 반환하므로, 이를 통해 한글 입력 중일 때 이벤트 처리를 막고, 조합이 완료된 후에만 메시지를 전송하도록 수정
또한, 영어 입력 시에는 조합 문자가 없으므로, 중복 이벤트 문제가 발생하지 않음
관련 코드
const handleKeyDown = (e) => {
// Enter 키를 누르고, altKey가 활성화되지 않았으며, IME 조합이 완료된 상태에서만 메시지 전송
if (e.key === "Enter" && !e.altKey && e.nativeEvent.isComposing === false) {
e.preventDefault();
sendMessage();
}
};🧨 소켓 메시지 전달 시 토큰 만료 확인 문제
소켓 메시지를 전달할 때 클라이언트에서 토큰값을 활용하여 사용자 인증을 수행했으나, 토큰 만료 여부를 실시간으로 확인하지 못하는 문제가 발생
토큰이 만료된 상태에서도 메시지가 전송되어 메세지가 전송이 되지않거나, 인증되지 않은 사용자가 접근할 가능성이 생김
- 원인
- 해결 방법
- 소켓 통신 자체에서는 토큰 만료 여부를 바로 확인할 수 없기 때문에, 만료된 토큰으로도 메시지가 전송되는 상황이 발생
- 서버로부터 토큰의 유효성을 검증받지 않고 메시지를 보내는 구조였기 때문에 발생한 문제
- 소켓 메시지를 전송하기 전에 토큰의 유효성을 확인하는 API를 호출하여, 유효한 토큰일 때만 소켓 메시지를 전송하도록 로직을 수정
- 토큰 검증 API를 호출한 후, 검증에 성공하면 메시지를 전송하고, 실패하면 재로그인 또는 에러 처리를 진행
관련 코드
const handleSendMessage = (chatMessage) => {
if (tokenCheck()) { // 토큰 검증 API 호출 ( true , false 반환 )
if (
client &&
connectionStatus === "CONNECTED" &&
chatMessage.trim() !== ""
) {
client.publish({
destination: `/app/message`,
body: JSON.stringify({
productId: productInfo.productId,
message: chatMessage,
type: "GENERAL_CHAT",
}),
headers: {
Access_Token: `Bearer ${token}`,
},
});
}
} else {
alert("잠시 후 다시요청하세요.");
}
};이를 통해 만료된 토큰으로 소켓 메시지가 전송되는 문제를 해결하고, 보안성을 강화할 수 있었음
🧨 Redux 유저정보 새로고침 시 데이터 소실 문제
Redux를 통해 관리하는 유저 정보가 페이지를 새로고침할 때마다 초기화되는 문제가 발생
유저 상태를 유지해야 하는데 새로고침할 때마다 정보가 날아가서 불편한 상황이 발생함
- 원인
- 해결 방법
- Redux는 상태 관리를 위해 사용하는 라이브러리지만, 상태는 메모리에 저장되기 때문에 페이지 새로고침 시 메모리가 초기화되어 Redux 상태도 함께 사라짐
- 이로 인해 Redux로 관리 중이던 유저 정보가 새로고침 시 유지되지 않고 초기화되는 현상이 발생
- 이 문제를 해결하기 위해 redux-persist 라이브러리를 적용함. redux-persist는 Redux 상태를 로컬 스토리지 또는 세션 스토리지에 저장하여 페이지 새로고침 시에도 상태가
유지되도록 도와줌
- 아래와 같이 redux-persist를 적용하여 Redux 상태를 로컬 스토리지에 저장하도록 설정
관련 코드
import { persistStore, persistReducer } from "redux-persist";
import storage from "redux-persist/lib/storage"; // defaults to localStorage for web
import { createStore } from "redux";
import rootReducer from "./reducers"; // root reducer import
const persistConfig = {
key: "root",
storage,
};
const persistedReducer = persistReducer(persistConfig, rootReducer);
export const store = createStore(persistedReducer);
export const persistor = persistStore(store);이제 유저 정보가 Redux에 저장되면, 새로고침 후에도 로컬 스토리지에서 해당 정보가 복구되어 상태가 유지됨
추가 조치: 로그아웃 시에는 persist 데이터를 삭제하여, 유저 데이터가 브라우저에 남아있지 않도록 함. 이는 보안적으로 민감한 유저 정보가 로그아웃 이후에도 남아있는 것을 방지함
관련 코드
const handleLogout = () => {
persistor.purge(); // 로그아웃 시 저장된 상태 삭제
// 기타 로그아웃 처리 로직...
};
🧨 폼 데이터와 멀티파트 데이터 문제
폼 데이터 전송 시 멀티파트 데이터로 전송해야 할 파일이 올바르게 처리 되지 않음
에러 메시지 400 Bad Request, 415 Unsuppoted Media Type 등
파일과 폼 데이터가 정상적으로 서버에 전송되어야 하는데 파일 업로드 실패 발생
- 원인
- 해결 방법
- 폼 데이터 : 일반적으로 텍스트 필드만 포함, application/json 형식으로 전송
- 멀티파트 데이터: 파일 업로드를 포함, multipart/form-data 형식으로 전송
- 잘못된 enctype 설정으로 인해 파일 선택 및 전송 과정에서 오류 발생
- 폼 설정 확인 후 형식 변경
- 파일 크기 제한 확인 : 서버에서 설정한 최대 파일 크기 초과하는 지 확인
- 브라우저 콘솔 로그 확인 : 네트워크 요청 및 응답을 확인하여 오류 메시지 검토
| 이름 | 분담 | GitHub | |
|---|---|---|---|
| FE | 류진식(Vice Leader) | 로그인, 회원가입, websoket을 활용한 실시간 채팅, 상품 조회, 검색, 상세내역 보기, 경매 등록 상품 관리 | https://github.com/ryujinsik |
| FE | 정은주 | 상품 등록기능, 회원정보 조회, 캐시 충전, 비밀번호 변경, 마이페이지 | https://github.com/25809637410 |
| BE | 박지은(Leader) | CI/CD, 입찰, 낙찰, 주문 기능, Web실시간 채팅 | https://github.com/je-pa |
| BE | 김채린 | 경매 상품 CRUD, 경매 상품 목록 필터링 조회, Scheduler를 사용한 경매 상품 상태 변경, 입찰자 기준 상품 인기순위 Top5 조회 | https://github.com/puclpu |
| BE | 배근우 | 찜 하기, 찜 취소, 찜 리스트, 찜 인기순위 Top5 | https://github.com/zz6331300zz |
| BE | 이아람 | 로그인, 회원가입, 토큰 관리, 캐시 충전, 회원정보 조회 | https://github.com/ramleeramlee |










