#TIL wecode Bootcamp Day 17(React 으로 변경한 인스타그램 사용 코드 정리)

Jung Hyun Kim·2020년 6월 13일
1

wecode

목록 보기
19/42

React 기능 정리 및 React로 다시 구현한 인스타그램 코드 리뷰🤹🏼‍♀️

기존에 TIL을 통해 사용한 코드에 대해서는 정리했지만 조금 더 자세하게 flow에 맞추어서 설명과 함께 정리하면서 한 주 간 배운 리액트에 대해 정리하려 한다.


Instagram

Instagram index.js page💙

  • 아직 라우터 사용법을 배우지 않아서 우선 index.html 페이지에 로그인과 메인 페이지를 동시에 구현 하고, 보여줄 화면 이외의 Login/Main Component는 주석처리 해서 localhost:3000 에서 띄워놓고 코드를 입력했다.

전체코드

import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import Login from './pages/Login/login';
import Main from './pages/Main/main';

ReactDOM.render(
    <Login />,
    // <Main />,
  document.getElementById('root')
);

부분 코드 정리

import React from 'react';
import ReactDOM from 'react-dom';
  • 먼저 react pacakge를 import 해온 다는 뜻이며 react 소스는, components, states, props 등 react 코드 전체를 포함한다.
  • react-dom package 는 이름에서 보여주는 React와 DOM요소를 붙여주는 패키지 이며, index.html에 ReactDOM.render()를 mount하는데 사용된다고 보면 된다
  • 나누게된계기는 React Native 라이브러리가 나오면서 React와 ReactDOM 라이브러리로 나뉘었다고 하는데, 그건 아마 React Native를 사용하게 된다면? 알게될것 같다!


import './index.css';
  • 공통 css를 index.css파일에 저장하고 그걸 import해왔다는 뜻인데 경로롤 보자면 index.html와 index.css 는 같은 src 파일 안에 위치함으로 현재 파일위치를 나타내는 ./이후파일이름을 입력함으로서 경로로 이동하도록 한다.
  • terminal 경로찾는 동일한 방법으로 계산해서 입력하면 된다. 파일이 많아져서 경로 찾기가 힘들어 진다면 VSCode에 path intelligence extension을 입력하면 좀 더 간편히 경로를 찾을 수 있다.

import Login from './pages/Login/login';
import Main from './pages/Main/main';
  • Login component와 Main component를 가져온다는 뜻이며 경로불러오는 법은 위에 설명과같이 현 파일을 기준으로 . ..사용을 통해 원하는 파일 위치를 입력하면 된다.

ReactDOM.render(
    <Login />,
    // <Main />,
  document.getElementById('root')
);
  • ReactDOM.render(a,b) 즉 a를 b에 보여줘 라는 내용인데, a 는 JSX형태로 입력해야하며 보통 들어갈 내용을 Component로 나누어관리해 각 Component안에 입력해준다. 먼저 react를 사용할 index.html<body><div Id="root></div></body>를 입력해 주었기 때문에 우리는 화면에 구현할 내용을 document.getElementById('root')에 보여줘 라고 나타내고 있는 것이다.

Login page 🆗

  • 조건 : 아이디 값에 '@' 포함, 비밀번호 수가 5자리 이상일때 버튼 활성화 할 것
  • 전체 코딩에서 사이사이에 설명을 통해 해당 코드에 대해 정리하고 넘어갈 것

전체코드

import React, { Component } from "react";
import './login.css';
import logo from '../../images/login/logo_text.png';

class Login extends Component {
  constructor() {
    super();

    this.state = {
      IDInput: "",
      passwordInput: "",
      ready: false
    }
  }

  idChangeHandler = e => {
    this.setState({
      IDInput: e.target.value
    })
  }

  passwordChangeHandler = e => {
    this.setState({
      passwordInput: e.target.value,
      ready: e.target.value.length > 5 && this.state.IDInput.includes("@") ? true : false
    })
  }

  clickHandler = e => {
    e.preventDefault();
    console.log("ID", this.state.IDInput)
    console.log("Password", this.state.passwordInput)
    // console.log(this.state.passwordInput.length&&this.state.IDInput.includes("@"))
  }

  render() {



    return (
      <div>
        <div class="whole-box">
          <div className="container">
            <div id="logo-holder">
              <img src={logo} alt="instgram logo" />
            </div>
            <form id="login-form">
              <div className="formcontainer">
                <input onChange={this.idChangeHandler} type="text" id="username-field" className="login-form-field" placeholder="전화번호, 사용자 이름 또는 이메일" />
                <input onChange={this.passwordChangeHandler} type="password" id="passwsord-field" className="login-form-field" placeholder="비밀번호" />
                <input className={this.state.ready ? "ready-to-submit" : "not-ready-to-submit"} onClick={this.clickHandler} type="submit" value="로그인" />
              </div>
            </form>
            <div className="linkContainer">
              <a className="forgotPW" href="#">비밀번호를 잊으셨나요?</a>
            </div>
          </div>
        </div>
      </div>
    );
  }
}


export default Login;

부분 코드 정리

import React, { Component } from "react";
  • {Component}를 추가해주는것은 아래 코드에 Component를 추가하겠다는 뜻
  • 첫 코드에 {Component} 로 추가해줄수도 있고, 첫 코드에 {} 포함하지 않고 class 정의할때 이렇게 표현할 수도 있다. class Login extends React.Component

import './login.css';
  • login 페이지 구현에 사용된 css를 css파일에 분리하여 import 해준다.
  • 파일 관리를 위해 login.css도 login.js파일과 같은 Login page 폴더 안에 관리하였기 때문에 ./login.css 로 작성이 가능하다.


import logo from '../../images/login/logo_text.png';
  • 페이지에 url이 아닌 저장된 이미지를 사용한 부분은 logo 를 import 해주고 logo가 들어가야 하는 위치에 {logo} 로 표현해준다.

  • JSX에 자바스크립트 코드로 들어가야할 내용은 항상 중괄호 안에 위치하게 한다 { }

  • 사용할 이미지 파일은 images폴더를 만들어서 별도로 관리해주었다. (사실 폴더를 만들만큼 사진이 많지는 않지만, 나중에 프로젝트 할땐 많을 거니깐 먼저 file 전체의 architecture를 잘짜는 연습이라고 생각하면 될 것같다!)


class Login extends Component {
  constructor() {
    super();

    this.state = {
      IDInput: "",
      passwordInput: "",
      ready: false
    }
  }
  • Component는 function Componentclass Component형태가 있는데, hook을 사용하는 더 편리한 function Component는 다음 프로젝트부터 적용해보도록 하고 금번 인스타그램 기능은 기본에 충실한 class Component로 구현해 보도록 한다.

  • Class component는 실행하자마자 constructor 함수가 실행되고 constructor 함수는 super() 와 꼭 함께 사용해야 한다. (
    공식 문서 설명 첨부

    In JavaScript classes, you need to always call super 
    when defining the constructor of a subclass. 
    All React component classes that have a constructor 
    should start with a super(props) call.
  • Componentconstructor 내부에 this.state 를 설정해서 Component의 상태를 정할 수 있다.

  • 먼저 로그인 시 사용자가 input 값을넣을 아이디 값, 비밀번호 값, 버튼 활성화 상태가 필요하므로 첫 state에 위와 같이 빈 stringfalse 로 설정해준다




  idChangeHandler = e => {
    this.setState({
      IDInput: e.target.value
    })
  }
 
  • 구현하고자 하는 기능은 사용자가 ID값을 넣었을때 그 값을 현재 상태로 지속적으로 업데이트를 해야 한다. ID/Password validation 확인 할때 그 저장된 값을 기준으로 확인이 가능하기 때문이다.

  • 아래 아이디가 들어가야 할 Input 태그 자리에 onchange 함수를 설정해주고, 값이 입력될때 설정을 해야 하므로 해당 함수는 event 값을 parameter로 받는 형태의 함수 구조를 만들어준다 idChangeHandler = e => {}

  • 사용자가 input 에 입력하는 value를 이전에 정의한 this.state.IDInput으로 업데이트 되게 하기 위해서는 this.setState 를 사용한다.

  • this.setState({IDInput = e.target.value}) 를 사용하면, 우리는 React에게 해당 component를 re-render 해줘 라고 말하는것이고 re-render 하면서 state값을 setState값으로 업데이트 하는 것이다.

  • 함수가 실행되면 아래 render() 이후의 this.state.IDInput이 업데이트 되는 것이므로 constructor() 와 render() 사이에 위치하게 한다.

  • 'setState`는 비동기 함수이다. 순서대로 실행 되지 않고 특정 이벤트가 실행 될때 실행된다고 보면 된다.



  passwordChangeHandler = e => {
    this.setState({
      passwordInput: e.target.value,
      ready: e.target.value.length > 5 && this.state.IDInput.includes("@") ? true : false
    })
  }
  • password 또한 이전에 ID 값 정의한 것처럼 정리해줄 수 있다.
  • password 또한 e.target.valuesetState를 저장해준다
  • ID/password validation을 위해 이전에 set 해준ready : false를 접근해 봅니다.(이름은 직관적으로 지어야 한다고해서 로그인 준비가 되었냐 말았냐의 ready를 썼는데.. 다른사람들 눈에도 그렇게 보일지는 모르겠다..)
  • login validation의 boolean 값이 들어가기 때문에 삼항 연산자를 통해 구현했다.
  • 즉 ready 의 값은 : 이후에 삼항연산자를 쓴 구조이다. (사실 처음에 이구조를 짰을때, 삼항 연산자에도 : 가 들어가서 이게 되나? 했는데 코드가 되길래 아하? 하면서 사용하게 되었다. 코드는 치면서 터득하는중.. 되는지 안되는지는 그냥 쳐보자 그럼 알수있다 !! )
  • ready: e.target.value.length > 5 && this.state.IDInput.includes("@") ? true : false
  • password의 length가 5가 넘고, ID input에 "@"가 들어가며 그 두 조건을 다 만족하면&& true가 되고 아니면 false가 된다.


  clickHandler = e => {
    e.preventDefault();
    console.log("ID", this.state.IDInput)
    console.log("Password", this.state.passwordInput)

  }
  • 입력 값이 콘솔에 보여주게 하는 기능을 추가했는데 사실상 화면상에는 보여지지 않는 내용이다.


 render() {
   return (
     <div>
       <div class="whole-box">
         <div className="container">
           <div id="logo-holder">
             <img src={logo} alt="instgram logo" />
           </div>
           <form id="login-form">
             <div className="formcontainer">
               <input onChange={this.idChangeHandler} type="text" id="username-field" className="login-form-field" placeholder="전화번호, 사용자 이름 또는 이메일" />
               <input onChange={this.passwordChangeHandler} type="password" id="passwsord-field" className="login-form-field" placeholder="비밀번호" />
               <input className={this.state.ready ? "ready-to-submit" : "not-ready-to-submit"} onClick={this.clickHandler} type="submit" value="로그인" />
             </div>
           </form>
  • 아래 render()안의 내용은 화면에 보여질 내용을 보여준다. 먼저 처음에 render()가 실행되고 this.함수명이 실행되면 re-render 되면서 변경된 setState값을 적용하는 순서라고 보면 된다.
  • 아래 내용이 더 있어서 중간부분만 보여주기때문에 최상위 요소가 없는데 render() { return()}형태로 되어있고 return()안에는 최상위 요소로 감싸야 한다. 보통은 <div></div> 혹은 <> </> react fragment형태로 작성한다.
  • onChangeonClick함수는 ={}형태로 중괄호 안에 표현해주며, 예를들어 안에 삼항연산자가 들어간 submit button의 경우 아래와 같이 표현한다.
  • <input className={this.state.ready ? "ready-to-submit" : "not-ready-to-submit"} -css 코드에 ready-to-submitnot-ready-to-submit`을 만들어 두고 각각 준비가 안됬으면 연한 파랑 컬러, 준비가 되었으면 진한 파랑컬러로 설정 해두고, true와 false 상태에 따라 변경되도록 한다.



            <div className="linkContainer">
              <a className="forgotPW" href="#">비밀번호를 잊으셨나요?</a>
            </div>
          </div>
        </div>
      </div>
    );
  }
}


export default Login;
  • 끝에는 작성한 component를 export 해서 부모component에서 사용,인용 될수 있도록 입력한다.



Instagram main page 💒

  • 조건 : 댓글 적고 게시 버튼을 누르거나, 'Enter'키를 입력 하였을 때 댓글이 입력 되도록 할 것

  • 이전에 기본적인 import 문법은 정리하지 않고 새로 생성된 부분만 정리 할것

  • 전체코드를 component화 하여 나누었고, feed 부분에만 react를

Main 전체코드

import React, { Component } from "react";
import './main.css';
import Header from "../.././components/Header.js";
import Feed from "../.././components/Feed.js";
import Rightfeed from  "../.././components/Rightfeed.js";




class Main extends Component {
    render() {
        return (
            <>
                <Header />
                <main>
                <Feed />
                <Rightfeed />
                </main>
            </>


        );
    }
}


export default Main;
  • 기존에 다른 요소 들은 거의 다 언급했기 때문에 return 내부에 component 나눈 부분만 보자면, 메인페이지를 크게 header, feed, rightFeed 세 부분으로 나누었고, feed와 rightfeed는 결국 main을 부모 요소로 두기 때문에 <main></main>안에 순서대로 입력해주었다.


Feed 부분 코드

  • Login 페이지 처럼 전체 코드넣고 부분 코드 넣는 식으로 하려 했지만 feed 페이지 꾸미는데 있는 JSX 코드가 많아서 실제로 기능구현에 사용된 JSX와 function 담긴 부분만 설명해보려고 한다.

  • 처음 코드를 만들었을때 comment : ""this.state에 추가했고, map함수는 쓰지 않고 댓글이 들어가야 할 자리에 {this.state.comment} 를 넣었다. changeHandler로 setState를 입력한 값으로 바꿨으니깐 하고 넘어갔는데, 실상은 그게 아니었다.

  • 댓글을 달면 계속 추가되어야 하는데. {this.state.comment}로 끝내버렸으니, 당연히 댓글은 한개밖에 작성할 수 없다.

  • 그렇다면 this.state.comment 해서 작성한댓글을 놔두고 새로 작성한 아이들이 순서대로 차곡차곡 보여주는 형식으로 해야하는데 어떻게 구현해야할까?

  • 작성하고 게시가 되면 그 게시된 글을 차곡차곡 빈 array에 넣어놓고 새로 추가하는 것들은 순서대로 출력 되도록 해야한다.

  • 이 기능을 구현하려면 . 그렇게 했을땐 댓글 한 개 추가는 가능했지만 그 이상을 넣어야 함으로 구글링 하다 해당 영상을 발견하고 코드에 많은 도움을 받았다.(https://www.youtube.com/watch?v=A2PKZlzbOtI&list=WL&index=2&t=0s)

  • comments 라는 빈 array (입력하는 값이 순서대로 담길 빈배열) 와 map 함수 : 배열하나하나를 돌면서 입력해준 조건이 적용된 배열을 출력하는 함수로 해결할 수 있었다.

        this.state = {
          comments : [],
          comment : ""
        }

  }
      changeHandler = (e)  =>{

          this.setState ({comment : e.target.value})  

        } 
      
  • textarea 에 입력함수로서 작성한 comment를 setState에 저장해주었다



      handleKeyPress = (e) => {
        e.keyCode === 13 && this.clickHandler()
      }
  • textarea에 OnKeyUp={this.handleKeyPress}이벤트를 주고 &&으로 항상 true만있는조건을 표현해 주었다. 예를들어 위의 코드를 삼항 연산자로 바꾸면
    e.keyCode===13? this.clickHandler() : null로 나타낼 수 있는데 null은 렌더링 하면 아무것도 보여주지 말라는 뜻이다. null 값처럼 false에아무것도 일어나지 않는다면 &&을 통해 조건부 렌더링이 가능하다.



      clickHandler = () =>  {
        let comments = this.state.comments
        let comment = this.state.comment
        comments.push(comment)
        this.setState({comments : comments})
      }
      
  • 여기서 작성한 comment들을 comments array에 넣어주게 되는데, 게시 버튼을 클릭함과 동시에 빈 배열인 comments에 push 함수를 통해서 추가하고 setState를 통해 배열을 새로운 comments 로 업데이트 한다.
  • 이 과정이 잘 이해되지 않아서 실제 페이지에서 react dev tool을 켜서 실시간으로 state에 들어가는 comment array를 확인하고 나니 이해가 잘 되었다.
  • react는 개발자 도구도 너무 보기 편리하고 이뻐서 좋다.. (벌써?)



<section className="new-comment">
{this.state.comments.map ((comment) => { return (
<span><strong>joanne_jhk</strong> {comment} </span>)})}</section>
  • 여기서 map함수가 나오는데 입력한 comments 가 있는 배열에서 setState에 업데이트 된 comment를 parameter로 받아 입력한 comment를 하나씩 화면에 하나씩 보여주게 한다. 간단한 이 맥락을 이해하는데 꽤 오랜시간이 걸렸ㄷ...ㅠㅠ 익숙해 지겠지?


전체 코드 깃허브 repository

https://github.com/junghyunhao/InstagramCloneinReact



다음주에는 fetch와 라우터 등 다양한 기능을 배워서 실제 백엔드와 붙여본다고하는데 너무 기대된다!!!👀👏🏼👏🏼👏🏼👏🏼

profile
코린이 프론트엔드 개발자💻💛🤙🏼

0개의 댓글