거래액 전세계 47위 암호화폐 거래소, 국내 최초로 이더리움 클래식을 상장, 사이버배상책임 보험 계약을한 거래소 코인원 클론 프로젝트
회원들의 매수 매도에 의한 정보를 통해 실시간 통신을 적용해 데이터를 계속 업데이트 해줘야 함, 그에 따른 실시간통신을 폴링방식(일정시간을 주기적으로 데이터를 받아오는 방식) 혹은 Socket 통신을 선택해야 했음
=> 결국 채팅(Websocket)을 제외한 나머지는 폴링 방식으로 하기로 함
실시간 통신이라는 부담감이 있었고 백엔드는 매수, 매수도를 구현해야 하는데 큰 어려움이 있을거라고 예상 함
처음에는 팀원이 6명이였지만 부득이한 사정으로 4명까지 줄어들었고 그에 따른 리스크는 나머지 팀원들이 감당해야 했기에 기능 구현하는데 큰 어려움이 있어 마지막까지 최선을 다했지만 많은 부분이 미흡 해 너무나도 아쉬운 프로젝트였다.
Back-End : Python, Django web framework, Beautifulsoup, Selenium, Bcrypt, My SQL
Common : email Link Authorization, AWS(EC2,RDS), RESTful API
프로젝트에서 내가 맡은 역할
코인원은 페이지 수가 그렇게 많지 않은 편이지만 세부적인 기능들이 많다는 것을 알게 됐다.
나는 로그인, 회원가입 페이지 및 코인들의 거래상황을 볼 수 있는 거래소 페이지를 맡게 됐다.
또한, 채팅은 실시간을 구현하기 위해 Websocket을 적용해야 했다
담당역할
CRA 초기 셋팅설정(jsconfig.json, 폰트설정, reset.css, 디렉터리 구조 설정, sass 설정, Router 설정)
로그인 및 회원가입 정규식 적용
Websocket(Express)를 이용한 실시간 채팅 구현
매수,매도 및 거래에 관한 기본 기능 구현
코인 별 데이터 가져오기
잘한점
원래는 채팅도 백엔드 쪽에서 소켓을 구현해서 적용하려고 했지만 백엔드 쪽에서 적용하기 어려워 내가 프론트쪽에서 Express를 이용하여 짧은 시간 안에 실시간 채팅을 구현하여 성취감이 너무 컸다
hooks, redux, Styled components를 학습하면서 프로젝트를 같이 개발하는 과정에서 짧은 시간이였지만 빨리 배워 적용할 수 있어서 너무 행복했다
회원가입 시 정규식이 5개 정도 사용 됐는데 정규식을 글로벌 파일로 따로 빼서 다 같이 사용할 수 있게 만든 점에서 별거 아니지만 나름 좋았다
아쉬운 점
제일 아쉬운점은 프로젝트 시작하자마자 부득이하게 프론트엔드에서 1명이 나가고, 2주차에서는 백엔드분이 1명 나가셔서 나머지 팀원들이 짧은 시간안에 많은 부분을 도맡아 해야하기에 완성도가 많이 떨어졌던 것은 사실이였다.
새로운 개념인 hooks, redux, Styled components를 적용하면서 개발했기에 그만큼 성장은 했지만 개발기간 안에서는 속도가 더뎠던 것은 사실이다.
폴링 방식 및 그래프 데이터, 코인 데이터에 대한 여러 요청이 한 꺼번에 있어 사이트가 매우 느려진다는 단점이 발생
해결 및 개선 방법
부득이한 사정으로 동료 개발자가 이탈 시 나름대로의 대책이 필요하다는 것을 느꼈다.
이러한 상황 속에서도 기능 구현에 있어서 오류 같은 것들을 확실하게 되집고 넘어가는 것이 필요하다는 것을 느꼈다.
백엔드 엔드포인트가 나오기전까지 기다리는 것보다 나름대로의 방법(mock data)를 사용해 속도에 제한받지 않고 개발할 수 있도록 해야겠다
소켓 통신 및 코드 구조를 좀 더 공부 해 데이터가 변경 사항이 있을 때만 fetch 요청을 하여 서버 부하를 줄이고 느려지는 현상을 방지하도록 해야 겠다.
프로젝트 하면서 기록하고 싶은 코드
1. 회원가입 및 로그인 정규식
사실 구글링하면 다 나오는 정규식이고 별거 아닌 것 처럼 보이지만 나름대로 정규식에 대해 공부할 필요성을 느끼게 된 계기이며, 나중에는 직접 정규식을 구현해 볼 생각이다.
이 정규식을 글로벌 변수화 해서 사용하면 팀원들도 개발할 때 편리할 것이다.
// 이메일 정규식// 이메일 정규식exportconst check_email =/([\w-\.]+)@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.)|(([\w-]+\.)+))([a-zA-Z]{2,4}|[0-9]{1,3})(\]?)$/;// 대문자 정규식exportconst upper_case =/[A-Z]/;// 소문자 정규식exportconst lower_case =/[a-z]/;// 숫자 정규식exportconst number_case =/\d/;// 3자리exportconstnumberFormat=x=>{
x = x.replace(/^[0]|[^0-9,]/g,"");// 입력값이 숫자 및 0으로 시작하는 것이 아니면 공백
x = x.replace(/,/g,"");// ,값 공백처리return x.replace(/\B(?=(\d{3})+(?!\d))/g,",");// 정규식을 이용해서 3자리 마다 , 추가};exportconstremoveComma=str=>{returnparseInt(str.replace(/,/g,""));};
2. useEffect 안에서 useState 사용 시 문제점
처음에 클래스형의 ComponentDidmount 처럼 useEffect를 사용하기 위해 두 번째 매개변수로 빈 배열을 주면 된다는 것을 알았지만 Eslint 자동 저장 때문에 배열안에 자동으로 변수를 기입하여 변수를 의존하게 만들었다.
const[msg, setMsg]=useState("");const[arr, setArr]=useState([]);useEffect(()=>{
socket.on("update message",obj=>{setArr(res);// res값을 받아서 setRes로 변경하는 의도지만 아래 배열안에 자꾸 res가 자동으로 기입});
bottom.current.scrollTo({ top: bottom.current.scrollHeight });},[res]);
2가지 해결 방법
1. 이펙트가 자급자족 하도록 만들기
const[msg, setMsg]=useState("");const[arr, setRes]=useState([]);useEffect(()=>{
socket.on("update message",obj=>{setArr(prev=>[...prev, obj]);// res값을 받아서 setRes로 변경하는 의도지만 아래 배열안에 자꾸 res가 자동으로 기입});
bottom.current.scrollTo({ top: bottom.current.scrollHeight });},[res]);
이전 상태를 기준으로 상태 값을 업데이트 하고 싶을 때는, setState 에 함수 형태의 업데이터를 사용하면 된다는 것을 알았다!!!
의존성을 제거하지 않고도 실제로 문제를 해결!!!(편법이 아닌 올바른 방법)
이펙트는 더 이상 랜더링 스코프에서 count 값을 읽어 들이지 않습니다.
res 는 우리가 setArr(res) 이라고 썼기 때문에 이펙트 안에서 필요한 의존성이었지만, 진짜로 우리는 res를 arr안에 불변성을 유지한 상태로 추가하고 돌려주는 것을 원한 것 뿐이다.
=> 한 마디로 res를 [] 안에 추가하여 의존성을 부여하고 싶지 않았을 뿐이다
이러한 경우 함수 형태의 업데이터를 사용하면 된다 (최신 방법)prev => [...prev, obj]
2. Eslint 자동 저장 강제로 해제
위의 사진 처럼 강제로 자동 입력 되는 부분을 비워 놓으면 아이콘이 뜨는데 누르고 Disable을 적용 시켜주면 빈배열인 상태로 적용 할 수 있다.
3. redux 적용
이번 프로젝트에서 처음으로 사용해본 redux 구조를 간단하게 설명하면서 상기 시켜보도록 하려고 합니다.
redux는 기본적으로 전역적으로 state를 관리하여 모든 컴포넌트에서 손쉽게 사용 및 변경할 수 있는 라이브러리 입니다.
1. redux 폴더 구조
src 폴더 아래 본인이 사용하려고 하는 Redux 폴더를 생성한다.
액션 생성함수는 Actions 폴더안에 index.js 안에서 모두 선언하여 관리한다.
reducer는 reducer 폴더를 따로 생성하여 관리한다(나중에 rootReducer로 묶어줌)
2. 액션 생성함수 정의
액션 생성함수는 store에 전역으로 관리되고 있는 state 값을 변경 시키기 위해 reducer가 필요한데 이 reducer의 액션 타입(state를 변경하는 타입)을 지정하고 해당 액션을 정의 해준다.
3. reducer 정의
reducer는 state 값을 변경하기 위해 사용 되는데 액션 타입에 따른 액션생성함수를 이용해 state 값을 변경 시킨다.
4. rootReducer로 recuder 묶기
위에서 정의한 reducer들은 같은 폴더안에 rootReducer를 생성하여 모두 감싸 준다.
5. 프로젝트에 redux 적용하기
위에서 감싸는 rootReducer를 포함한 createStorem, Provider를 사용하여 위와 같이 Provider로 프로젝트를 감싸주고 Provider에 변수로 선언한 리듀서를 적용 시켜주면 완성이다.