1차 프로젝트 회고록

ceres·2020년 3월 9일
0

프로젝트

목록 보기
3/3

(2020/3/8)

1. 프로젝트소개

팀명: BR-Sariwon
내용: 베스킨라빈스 사이트 클론
인원: 총 5명 (프런트3명, 백앤드2명)
기간: 2주 (2020/2/24-2020/3/6)
코드: https://github.com/wecode-bootcamp-korea/BR-Sariwon-frontend
영상: https://www.youtube.com/watch?v=U8hu7C2NRjE&feature=youtu.be

2. 사용된기술

  • Front-End : JavaScript, React, Sass
  • Back-End : Django, Python , Beautiful Soup, Pandas, Bcrypt, JWT,
  • Deployment : AWS, EC2, RDS, CORS headers

3. 내가 맡은 역할/부분

  • 로그인페이지 (api요청-post, 토큰저장, 유지, 삭제)
  • 회원가입페이지 (정규식사용)
  • 메뉴 목록 페이지 (api요청-get, map함수사용)

4. 잘한점+ 아쉬운 점 + 해결/개선방법

새로 배운 것

  • react 초기세팅 경험
  • git 사용법 익힘
  • scss 사용
  • scrum 방식으로 프로젝트 진행 (standup-meeting, Trello 사용)
  • api연결(fetch함수)
  • 3항연산자
  • 조건에 따라 class변화주기
  • map함수 사용
  • event 함수 사용 (onClick, onChange)
  • 리액트 생명주기 개념 이해한것 (아직 정확히 익히지는 못함)
  • 정규식 사용 (회원가입)
  • routes 사용방법
  • 백엔드와 소통 방법
    1) 프런트에서 어디까지 구현할 것인지 정확히 알려줘야한다.프런트가 어디까지 구현할 것이지에 대해 소통이 되지 않으면, 백엔드는 필요없는 데이터를 구축할 수 도 있고, 프런트가 구현했는데 데이터가 준비되지 않는 경우도 있다.
    2) 필요한 데이터의 형식에 대해 소통해야한다. 회원가입을 구현하면서 프런트에서 원하는 데이터 형식이 있었다. 이메일은 @와.이 들어가야한다던가, 비밀번호는 영문자,숫자,특수문자가 들어가야한다는 등. 백엔드가 필요한 데이터 형식을 알고 있다면 프런트에서 사용자가 해당 데이터형식에 맞는 정보를 입력하도록 만들 수 있다.
  • 터미널에서 ctrl+c를 누르면 서버가 끊긴다.
    (그냥 git 사용할 수 있게 해주는 기능인줄 알았는데, ctrl+c 를 누르면 서버가 계속 끊겨 npm run start를 계속 하게 되더라.. 서버가 끊기게 하고 싶지않다면 vsc에서 터미널을 2개키고 한쪽에서는 git을 사용하고 한쪽에서는 서버를 계속 켜두면 된다.)

아쉬운 점

  • props로 값 넘겨주는것 숙지 못함
  • children 숙지 못함
  • distructuring 사용 숙지 못함
  • 애니메이션 기능 사용해보지 못함
  • api 주소 config.js로 관리하고 변수를 써서 사용하는 것 숙지 못함
  • 문법에 약해서 로직을 잘 짜지 못함
  • 라이브러리 사용 못해봄
  • Postman 사용방법 숙지 못함
  • 체력관리

해결/개선방법

  • 숙지 못한 부분은 많이 사용해보는게 답이라고 생각한다. 다음 프로젝트 때 사용 횟수를 늘려보자.
  • 문법이 약한건 매일 repl.it javascript 풀었던것을 다시 보는 풀어보는것으로 연습해보자.
  • 그리고 매일 근력운동좀...💪 살은 찌고 체력을 떨어지고...앞으로 이렇게 바쁜 날들이 많을텐데 이때마다 체력관리를 놓을 순 없으니까..운동 좀 하렴 나자신.

5. 기록하고 싶은 코드/함수/로직

1) Api 연결 (fetch함수)

  • post
//sign-up api 연결
  handleFetch = () => {
    fetch(`${URL}/account/sign-up`, {
      //URL에는 백엔드 서버주소가 들어간다. 위는 config.js에 주소를 넣고, 그 주소를 변수로 사용한 예이다. 
      method: "POST",
      body: JSON.stringify({
        name: this.state.name,
        email: this.state.email,
        password: this.state.pwd,
        address: this.state.address,
        phone: this.state.phone
      })
      //body에 백엔드에 보내줘야할 state값을 넣는다. 
    })
      .then(response => {
        if (response.status === 200) {
          this.props.history.push("/login");
          // response에 access가 있으면 login창으로 이동.
        } else if (response.message === "DUPLICATE_EMAIL") {
          alert("이메일이 중복되었습니다.");
          //이메일이 중복되었을 경우, DUPLICATE_EMAIL 메세지가 오는데, 이때 alert창이 뜨도록 코드를 짰다. 
        } else {
          alert("회원가입을 다시 진행해주세요");
        }
        return response;
      })
  };
//login api연결 : login값을 보내주고 토큰을 저장하면 된다.
loginFetch = () => {
    fetch(`${URL}/account/sign-in`, {
      method: "POST",
      body: JSON.stringify({
        email: this.state.id,
        password: this.state.pw
      })
    })
      .then(response => response.json())
  //백엔드에서 보내주는 data를 jason형식으로 받는다. 
      .then(response => {
        if (response.Authorization) {
          localStorage.setItem("token", response.Authorization);
          this.props.history.push("/");
          //response.Authorization에 토큰이 있으면 localStorage에 token을저장하고
       	 // 메인페이지로 간다.
          window.location.reload();
          //토큰이 저장되면 login버튼이 logout 버튼으로 화면이 바뀌어야 하는데 reload를 해야만 화면이 바뀌어 강제로 reload를 해주었다.
        } else {
          alert("로그인을 다시 진행해주세요");
        }
        return response;
      })
  };
  • get
//itemlist data를 백엔드에게 받는 api 이다. 
//앤드포인트에서 type=숫자 숫자에 따라 아이스스크림, 케이크, 음료 각기 다른 데이터가 온다.
  itemListFetch = () => {
    const menuType = this.props.location.search.split("=")[1];
    //window.location.search를 입력하면 type=숫자 가 나온다. 
    //이것을 '='을 기준으로 나누면 '='앞이 [0]번째. 즉 type이라는 문자가,
    //뒤가 [1]번째가 된다. 즉, [1]번째에는 숫자가 들어간다. 
    fetch(`${URL}/product/menu?type=${menuType}`, {
      method: "GET"
      //데이터를 가지고 올때는 get 임을 잊지 말자
    })
      .then(response => response.json())
      .then(response =>
		//생략
          })
        )
      );
  };

2) 정규식 사용

checkPW = () => {
    const check_num = /[0-9]/; 		//숫자
    const check_eng = /[a-zA-Z]/;	//대소문자
    const check_spc = /[~!@#$%^*()_]/; //특수문자
    // 이것이 정규식이다.

    if (
      check_num.test(this.state.pwd) &&
      check_eng.test(this.state.pwd) &&
      check_spc.test(this.state.pwd) &&
      this.state.pwd.length >= 8
    ) {
      this.setState({ pwdCheck: true });
      //위의 식이 모두 true일 경우 state값에 변화를 주었다. 
    } else {
      this.setState({ pwdWrong: true });
    }
  };

3) 삼항연산자, 조건에 따른 class 변화

//emilCheck가 true/false 일때 class 값을 다르게줘서 true일때만 이미지가 나타나도록 함
<img
className={
  this.state.emailCheck
  ? "circle-tick show"
  //css에 className이 show일때 display:block을 주었다.
  : "circle-tick hide"
}
alt="circle-tick"
src="https://img.icons8.com/officel/40/000000/checked.png"
/>

4) map함수 (props 함수)

// 부모 컴퍼넌트(api로 데이터 이미 받은 상태, state에 data 이미 저장되어있음)
{this.state.data &&   //state에 data가 있는 경우 map함수를 돌리겠다. 
  this.state.data.map(item => {	
  return (
    <ItemBox		//ItemBox라는 컴포넌트를 반복함(map돌림)
    id={item.id}
    name={item.name}
	thumbnail={item.thumbnail}
	tags={item.tags.map((el, i) => i < 2 && el.name)}
	/>
);
})}
//자식 컴퍼넌트
<div className="name">
  <span>{this.props.name}</span>  
//부모 컴퍼넌트에서 자식컴퍼넌트로 전달할때는 props를 사용한다! {중괄호}도 잊지말자!
</div>
	<span className="item-img">
  		<img src={this.props.thumbnail} alt="" />
	</span>
</div>
             

4) 여려개 img data파일로 저장하고 한번에 불러오기

먼저 data가 들어간 js 파일을 만든다

const Img = [
  {
    benner:
 "http://www.baskinrobbins.co.kr/asset/images/menu/bg_icecream_menu.gif",
    title: "http://www.baskinrobbins.co.kr/assets/images/menu/h_title_A.png"
  },
  {
    benner: "http://www.baskinrobbins.co.kr/asset/images/menu/bg_icecreamcake_menu.gif",
    title: "http://www.baskinrobbins.co.kr/assets/images/menu/h_title_B.png"
  },
  {
    benner:
 "http://www.baskinrobbins.co.kr/asset/images/menu/bg_beverage_menu.gif)",
    title: "http://www.baskinrobbins.co.kr/assets/images/menu/h_title_C.png"
  }
  ];
export default Img;

불러오고 싶은 파일에서 data파일을 import 해준다.

import IMG from "Components/ItemList/Data/Data";
//import 해올 이름은(IMG) 꼭 data파일의 변수명(img)일필요는 없다. 

그후 data파일이 들어갈 곳에 {}를 사용하여 넣어준다.

<img className="header-benner" src={IMG[initIndex].benner} alt="" />
  //initIndex 자리에는 숫자가 들어간다.
  //예로 initIndex=1이라면, 배열로 된 data 객체에서 첫번째 배열의 benner프로퍼티의 값을 가지고 오라는 뜻이다.  

5) css 화면보다 큰 이미지를 화면사이즈에 맞추기

itemlist page에서 banner 이미지가 화면사이즈 보다 커서 중앙정렬하기가 어려웠다. 그때 멘토님께서 알려주신 방법이다.
object-fit: cover;
objet-fit은 지정된 너비와 높이에 맞게 장착되는 방법이다.
속성값으로 cover을 넣으면 내용이 종횡비를 유지하면서 정의된 너비와 높이를 가득 채울때까지 확대된다.

6) 리액트 생명주기 (ComponentDidMount, ComponentDidUpdate)

완정히 이해하지는 못하였다. 아래는 이해를 한 내용일 뿐, 정확하다고는 못하겠다. (사진은 예리님 블로그에서 getget)

ComponentDidMount: 단 한번 실행한다.
ComponentDidUpdate: 조건에 변화가 있을때마다 실행한다.
아래 배라 ItemList page를 보면 먼저 처음에 itemlist data가 백엔드로 부터 와야한다. 그럼 ComponentDidMount에서 data api를 fetch해오면 된다.
하지만 MENU 목록을 아이스크림, 케이크, 음료, 커피, 디저트로 바꿀때마다 다른 itemlist data가 필요한데, 이때는 fetch함수가 처음에 한번 실행되는 것이 아니라 조건이 바뀔 때 마다 (MENU목록을 바꿀때 마다) 실행되어야한다. 이때 사용 되는 것이 ComponentDidUpdate이다.

0개의 댓글