<< 학습 목표 >>

1. Context가 필요한 상황을 설명할 수 있다.

2. useContext 훅을 사용할 수 있다.


전 글 ( https://codingaja.tistory.com/84 ) 에서 주소록을 추가 하고 출력할 때 컴포넌트 간에 데이터를 전달하는 방법을 사용했음

1. 자식 컴포넌트에서 부모 컴포넌트로 데이터를 전달할 때는 부모 컴포넌트에서 setter 함수를 전달해야함

  자식 컴포넌트는 부모 컴포넌트가 전달한 setter 함수를 사용해 값을 전달함

2. 부모 컴포넌트에서 자식 컴포넌트로 데이터를 전달할 때는 props 를 사용해 전달함

 

이번에는 Context 배우면서 Context와 useContext 훅으로 컴포넌트 간에 데이터를 주고 받는 방법을 배우자

우선 Context와 useContext 훅이 필요한 상황을 알아보자

 

우리가 지금 만들고 있는 컴포넌트 구조는 굉장히 간단함

우리가 그린 컴포넌트 구조(왼쪽 그림)를 일반적으로는 컴포넌트 트리(tree / 나무) 라고 부름

컴포넌트 트리 라고 부르는 이유는 컴포넌트 구조를 왼쪽으로 90도 틀면(오른쪽 그림) 부모 컴포넌트가 제일 밑, 자식 컴포넌트가 위로 향하는데 부모 컴포넌트가 나무의 뿌리처럼 밑에 박히고 자식 컴포넌트가 잎처럼 위로 향하기 때문 

 

프로젝트가 점점 규모가 커져 아래와 같이 자식 컴포넌트가 생긴 상황이라고 해보자

이렇게 자식 컴포넌트가 많아진 상황을 "컴포넌트 트리가 깊어졌다" 라고 표현함

컴포넌트 트리가 깊어졌을 때 가장 마지막 자식 컴포넌트(어떤 컴포넌트4)의 값을 최상위에 있는 AddressMng 컴포넌트로 전달해야한다면?

 

1. AddressMng 컴포넌트에서 값을 전달 받기 위해 setter 함수를 AddressAdd 컴포넌트로 전달함

2. AddressAdd 컴포넌트는 부모 컴포넌트가 전달한 setter 함수를 어떤 컴포넌트1 자식 컴포넌트에게 전달함

3. 어떤 컴포넌트1 컴포넌트는 어떤 컴포넌트2 자식 컴포넌트에게 setter 함수를 전달함

4. 어떤 컴포넌트2 컴포넌트는 어떤 컴포넌트3 자식 컴포넌트에게 setter 함수를 전달함

5. 어떤 컴포넌트3 컴포넌트는 어떤 컴포넌트4 자식 컴포넌트에게 setter 함수를 전달함

6. 어떤 컴포넌트4 컴포넌트는 전달 받은 setter 함수를 사용해 값을 저장함

 

일반적인 프로젝트에서는 컴포넌트 트리가 굉장히 깊기 때문에 위 상황 보다 더 많은 컴포넌트를 거쳐야함

 

이렇게 최하위에 있는 자식 컴포넌트가 최상위에 있는 부모 컴포넌트로 값을 전달하는 경우에 setter 함수를 전달하려면 수도 없이 많은 컴포넌트를 거쳐야하기 때문에 하나라도 실수가 있다면 전체 프로젝트는 먹통이됨

 

컴포넌트 트리가 깊을 때 컴포넌트 간에 데이터를 주고 받기 위해 나온것이 Context와 useContext 훅임

Context와 useContext 훅을 사용하면 어느 컴포넌트든 컴포넌트 트리에만 있다면 Context에 값을 저장할 수 있고 Context에 저장된 값을 꺼낼 수 있음


잠깐 주소록 프로젝트는 두고 Context와 useContext 훅을 연습해보자

프로젝트 -> src -> chapter03 -> Component22.jsx 파일을 추가하고 아래 코드를 추가하자

import React from 'react';
import Component23 from './Component23';

export const MyContext = React.createContext();

function Component22() {
    return(
        <div>
            <MyContext.Provider value="Hi">
                <Component23 />
            </MyContext.Provider>
        </div>
    );
}

export default Component22;

 

그리고 이어서 프로젝트 -> src -> chapter03 -> Component23.jsx 파일을 추가하고 아래 코드를 추가하자

import React, {useContext} from 'react';
import { MyContext } from './Component22';

function Component23() {
    const value = useContext(MyContext);

    return(
        <div>
            <h1>Component23 컴포넌트입니다</h1>
            <p>부모 컴포넌트가 전달한 값은 {value} 입니다~!</p>
        </div>
    );
}

export default Component23;

 

<< 코드 설명 >>

(1). 부모 컴포넌트는 자식 컴포넌트들이 공용으로 사용할 변수를 선언함

  이름이 MyContext인 공용 변수 선언, 일반적으로 공용 변수의 첫 글자는 컴포넌트처럼 대문자로 시작함

  또한 자식 컴포넌트들이 공용 변수를 사용할 수 있도록 반드시 export 해줘야함

(2). 공용변수를 사용할 자식 컴포넌트를 지정

  이 안에 있지 않은 자식 컴포넌트는 공용 변수를 사용할 수 없음

  공용 변수를 사용할 권한을 주는거라고 생각하면 됨

  특히, 자식 컴포넌트들을 또 다른 말로 Consumer 컴포넌트라고 부름

(3). 공용 변수를 사용할 권한이 있는 자식 컴포넌트에서 공용 변수를 사용하려면 반드시 불러와야함

(4). 불러온 공용 변수를 사용하기 위해서는 useContext 훅을 사용해 풀어 변수에 저장해야함

(5). 공용 변수로 전달 받은 값을 출력함

 

Context와 useContext 훅을 사용해서 컴포넌트 간에 데이터를 교환한다고 했는데 정확히 얘기하면 컴포넌트 간에 데이터를 교환하기 위해 공용 변수를 선언하는건 createContext가 담당을 하고 공용 변수를 사용하기 위해서 useContext 훅을 사용하는 것


위 코드설명의 (2) 에서 Provider의 값이 변하면 Provider 안에 있는 모든 Consumer 컴포넌트들은 재렌더링됨


여기에 useState, useEffect, useRef 등 지금까지 배운 훅들을 조합해서 활용하면 더 넓게 활용할 수 있음

728x90
LIST

<< 학습 목표 >>

1. 컴포넌트 구조를 그릴 수 있다.

2. 자식 컴포넌트에서 부모 컴포넌트로 값을 전달할 수 있다.


전 글 ( https://codingaja.tistory.com/83 ) 에서는 페이지를 나누는거에 초점을 맞췄다면 이번에는 기능 구현에 초점을 맞추자

전 글에서 페이지를 나눈 걸 시각화 하면 다음과 같음

 

AddressAdd 컴포넌트에서 [ 추가 ] 버튼의 기능을 구현하자

 

AddressAdd 컴포넌트에서 이름, 연락처, 주소를 입력하고 [ 추가 ] 버튼을 누르면 AddressAdd 컴포넌트를 지나 AddressMng 컴포넌트를 지나 AddressList 컴포넌트로 전달되야함

 

AddressList 컴포넌트에서는 전달 받은 주소 정보를 addressList 변수에 저장해야함

그렇게 되면 addressList 컴포넌트에 변화가 생겼으므로 기존 AddressList 컴포넌트가 새 AddressList 컴포넌트로 교체되면서 추가된 정보를 갖고 있는 Address 컴포넌트들이 만들어질 것


AddressMng 컴포넌트가 가장 위에 있으므로 부모 컴포넌트라 부르고 AddressAdd, AddressList 컴포넌트는 자식 컴포넌트라 하자

AddressMng 컴포넌트 기준으로 Address 컴포넌트는 자식의 자식인 자손 컴포넌트이지만 AddressList 컴포넌트를 기준으로 Address 컴포넌트는 자식 컴포넌트임

 

현재 상태에서 사용자가 입력한 주소 정보를 전달하는 과정을 다시 얘기하면 AddressAdd 자식 컴포넌트에서 AddressMng 부모 컴포넌트로 값을 전달(1)해야하고 AddressMng 부모 컴포넌트는 AddressList 자식 컴포넌트로 값을 전달(2)해야하는 상황임

 

이렇게 자식 컴포넌트에서 부모 컴포넌트로 값을 전달 하고자 할 때는 부모 컴포넌트에서 자식 컴포넌트로 useState로 생성한 변수의 setter 를 전달해주면 됨

그리고 자식 컴포넌트에서는 부모 컴포넌트가 전달한 setter 를 사용해 전달 할 값을 저장하면 됨

이와 같이 구현한 코드를 입력하자

 

AddressMng 컴포넌트와 AddressAdd 컴포넌트에 아래와 같이 코드를 추가하자

 

<< 코드 설명 >>

(1). AddressMng 컴포넌트에서 useState, useEffect 훅을 사용하기 위한 import

(2). AddressAdd 자식 컴포넌트에서 추가한 주소 정보를 받아오기 위한 address 변수 선언

(3). AddressMng 부모 컴포넌트에서 AddressAdd 자식 컴포넌트로 address 변수의 값을 바꿀 수 있는 setter 함수 전달

  이때 전달한 setter 함수는 setAddress가 아닌 handleChangeAddress임

  이렇게 함수를 변수에 담아 전달할 수 있는 이유는? JS의 함수는 1급 객체이기 때문에

  만약 1급 객체란 말을 모르면 JS 1급 객체 와 관련된 키워드로 검색해보자

(4). AddressAdd 자식 컴포넌트에서 useRef 훅을 사용하기 위한 import

(5). 이름, 연락처, 주소 input 태그를 저장하기 위한 ref 변수 선언

(6). 이름, 연락처, 주소 input 태그를 ref 변수에 저장

(7). [ 추가 ] 버튼을 클릭하면 clickAddBtn 함수 호출

(8). 이름, 연락처, 주소 input 태그에 입력한 값을 가져와 JSON 객체에 담은 뒤 address 변수에 저장

  그 후 전달 받은 setter 함수에 JSON 객체 저장

(9). AddressAdd 자식 컴포넌트에서 전달받은 setter 함수를 사용해 주소 정보를 저장하면 AddressMng 부모 클래스의 address 변수에 변화가 생기므로 useEffect 내 함수가 동작해 AddressAdd 자식 컴포넌트가 전달한 값을 부모 클래스에서 확인할 수 있음

 

(9)는 부모 컴포넌트에서 자식 컴포넌트가 전달한 값을 받을 수 있나 확인하기 위한 훅임

 

이제 웹 페이지에서 개발자 모드의 [ console 패널 ] 을 열고 이름, 연락처, 주소를 입력하고 [ 추가 ] 버튼을 눌러보자


AddressMng 부모 컴포넌트에서 AddressAdd 자식 컴포넌트가 전달한 주소 정보를 전달 받았으므로 이제 AddressMng 부모 컴포넌트에서 AddressList 자식 컴포넌트로 값을 전달(1)하면 됨

AddressMng 부모 컴포넌트에서 확인용으로 추가해둔 useEffect 훅은 삭제하자

 

AddressList 자식 컴포넌트에서는 AddressMng 부모 컴포넌트가 전달한 주소 정보를 받기 위해 props 를 선언(1)하자

또한 주소 정보 목록(주소록) 을 저장하기 위해 useState로 addresList 변수를 선언함(2)

AddressMng 부모 컴포넌트에서 값을 전달하면 props에 변화가 생기므로 useEffect 훅으로 props의 변화를 감지하자(3)

props에 변화가 생기면 useEffect 내 코드가 동작(4)하고 AddressMng 부모 컴포넌트가 전달한 주소 정보가 있다면(5) 기존의 주소록을 복사한 새로운 주소록을 생성(6)하고 새로운 주소록에 전달 받은 주소 정보를 추가(7) 한 뒤 기존의 주소록을 새로운 주소록으로 교체(8)함

 

이제 AddressMng 컴포넌트 내에서 이름, 연락처, 주소를 입력한 후 [ 추가 ] 버튼을 눌러보자

그럼 주소 정보가 AddressAdd 자식 컴포넌트에서 AddressMng 부모 컴포넌트로, AddressMng 부모 컴포넌트에서 AddressList 자식 컴포넌트로 전달된 후 AddressList 컴포넌트 내 useEffect 훅을 통해 addressList 배열에 새로운 주소 정보가 추가되고 주소 정보가 출력됨

728x90
LIST

<< 학습 목표 >>

1. 프로젝트에 부트스트랩을 추가할 수 있다.

2. 하나의 컴포넌트를 여러 컴포넌트로 분리할 수 있다.

3. map 함수를 사용해 컴포넌트들을 출력할 수 있다.


지금까지 배운 내용들을 활용해 아래와 같은 주소록 관리 페이지를 만들어보자

1. 이름, 연락처, 주소를 입력하고 [ 추가 ] 버튼을 누르면 아래 목록의 마지막에 새로운 주소 정보가 추가됨

2. 수정 버튼을 누르면 해당 하는 주소 정보의 이름, 연락처, 주소가 수정 가능한 상태로 바뀜

  그 상태에서 이름, 연락처, 주소를 수정한 뒤 [ 완료 ] 버튼을 누르면 수정이 완료됨

3. 삭제 버튼을 누르면 [ 확인 ] [ 취소 ] 창이 나오며 [ 확인 ] 버튼을 누르면 해당 주소 정보가 삭제됨


CSS는 최대한 부트스트랩을 사용할 것임

 

먼저 프로젝트에 부트스트랩을 적용하자

인터넷에 부트스트랩 으로 검색(1) 해 첫 번째 결과 페이지(2)로 들어가자

결과 페이지가 다르다면 직접 들어가자 / https://getbootstrap.kr/

 

부트스트랩 적용하기 전 부트스트랩의 버전(1)을 확인하자

이 글을 보는 시점에 부트스트랩 버전이 다르다면 버전(1)을 클릭해 5.2 또는 버전 내 [ 모든 버전 ] 을 클릭해 5.2 버전으로 이동 하자

부트스트랩 버전이 5.2인 걸 확인하고 문서(2) 로 들어가자

 

문서 페이지로 들어가면 [ 빠른 시작 ] 이 보이는데 [ 빠른 시작 ] 내 2번(1) 안에 있는 link 태그(2)와 script 태그(3) 를 프로젝트에 복사 & 붙여넣기 해야함

 

이제 프로젝트 -> public -> index.html 을 열자

 

index.html 파일 내 head 태그 안에다가 문서에서 봤던 link 태그를 넣자

head 태그 안에만 있으면 됨

 

또한 index.html 파일 내 닫는 body 태그(</body>) 직전에 문서에서 봤던 script 태그를 넣자

 

이제 프로젝트에 부트스트랩 적용이 됐음


이제 주소록 관리 프로젝트 샘플 페이지를 추가하자

 

프로젝트 -> src -> chapter03 -> AddressMng.jsx 파일을 추가하고 아래 코드를 추가하자

import React from 'react';

function AddressMng() {
    return(
        <div className="container">
            <div className="text-center">
                <h1>[ 주소록 관리 프로젝트 ]</h1>
                <hr />
            </div>

            <div id="add_panel">
                <div className="row align-items-center justify-content-center">
                    <div className="col-2">
                        <input type="text" className="form-control" placeholder="이름" />
                    </div>
                    <div className="col-2">
                        <input type="tel" className="form-control" placeholder="연락처" />
                    </div>
                    <div className="col-5">
                        <input type="text" className="form-control" placeholder="주소" />
                    </div>
                    <div className="col-2 text-center">
                        <button type="button" className="btn btn-primary">추가</button>
                    </div>
                </div>
                <hr />
            </div>

            <div id="list_panel">
                <table className="table text-center">
                    <thead>
                        <tr className="row">
                            <th className="col-1" scope="col"></th>
                            <th className="col-2" scope="col">이름</th>
                            <th className="col-2" scope="col">연락처</th>
                            <th className="col-5" scope="col">주소</th>
                            <th className="col-2" scope="col"></th>
                        </tr>
                    </thead>
                    <tbody>
                        <tr className="row">
                            <th className="col-1" scope="row">1</th>
                            <td className="col-2">홍길동</td>
                            <td className="col-2">010-1111-1111</td>
                            <td className="col-5 text-left">서울특별시</td>
                            <td className="col-2">
                                <div className="btn-group" role="group" aria-label="Basic example">
                                    <button type="button" className="btn btn-success">수정</button>
                                    <button type="button" className="btn btn-danger">삭제</button>
                                </div>
                            </td>
                        </tr>
                        <tr className="row">
                            <th className="col-1" scope="row">2</th>
                            <td className="col-2" >고영희</td>
                            <td className="col-2" >010-2222-2222</td>
                            <td className="col-5" >인천광역시</td>
                            <td className="col-2">
                                <div className="btn-group" role="group" aria-label="Basic example">
                                    <button type="button" className="btn btn-success">수정</button>
                                    <button type="button" className="btn btn-danger">삭제</button>
                                </div>
                            </td>
                        </tr>
                        <tr className="row">
                            <th className="col-1" scope="row">3</th>
                            <td className="col-2">김철수</td>
                            <td className="col-2">010-3333-3333</td>
                            <td className="col-5">경기도</td>
                            <td className="col-2">
                                <div className="btn-group" role="group" aria-label="Basic example">
                                    <button type="button" className="btn btn-success">수정</button>
                                    <button type="button" className="btn btn-danger">삭제</button>
                                </div>
                            </td>
                        </tr>
                    </tbody>
                </table>
            </div>
        </div>
    );
}

export default AddressMng;

 

이 코드는 샘플 코드로 이 컴포넌트를 출력해보자


위 코드에서 역할별로 컴포넌트를 분리하자

먼저 사용자가 이름, 연락처, 주소 를 입력하고 [ 추가 ] 를 하는 영역이 있으므로 이 부분을 컴포넌트로 분리하자

 

프로젝트 -> src -> chapter03 -> AddressAdd.jsx 파일을 추가하고 아래 코드를 추가하자

그 후 AddressMng 컴포넌트에서 해당 부분을 AddressAdd 컴포넌트로 교체(1)하자

 

이번에는 주소 목록을 보여주는 영역이 있으므로 이 부분을 컴포넌트로 분리하자

 

프로젝트 -> src -> chapter03 -> AddressList.jsx 파일을 추가하고 아래 코드를 추가하자

그 후 AddressMng 컴포넌트에서 해당 부분을 AddressList 컴포넌트로 교체(1)하자

 

AddressList 컴포넌트에서 다시 컴포넌트로 분리할 수 있는 부분이 있음

주소 정보가 출력되는 각 행

프로젝트 -> src -> chapter03 -> Address.jsx 파일을 추가하고 아래 코드를 추가하자

그 후 AddressList 컴포넌트에서 해당 부분을 Address 컴포넌트로 교체(1)하자

 

눈에 보이는 외관에 변화는 없지만 코드는 상당히 체계가 잡혔음


일단 지금 유심히 봐야할 부분은 AddressList에서 주소 정보가 들어있는 addressList 변수에 map을 사용했다는 점

리엑트에서 map함수를 사용해 Address 컴포넌트를 출력하고 있다는 점

 

앞선 글 ( https://codingaja.tistory.com/76 ) 에서도 봐서 map 함수에 대해서 잘 알고 있겠지만 데이터들을 갖고 있는 변수를 사용해 컴포넌트를 출력할 때는 map 함수를 사용함(1)
map 함수를 사용해 컴포넌트를 출력할 때 컴포넌트에 반드시 key를 전달해야하며 key는 컴포넌트 간에 구분할 수 있는 유일한 값이어야함

유일한 값은 데이터들의 인덱스 번호이므로 인덱스 번호를 전달(2)하고 있음

 

map 함수를 사용해 컴포넌트를 반환하면 컴포넌트 배열이 만들어짐(1)

리엑트가 이 컴포넌트 배열을 화면에 출력할 수 있는 태그들로 변환해 화면에 출력되는 것(2)

 

728x90
LIST

<< 학습 목표 >>

1. useRef 훅을 사용해 태그를 저장할 수 있다.

2. useRef 훅을 사용해 저장한 태그를 핸들링할 수 있다.


useState 익히기 글 ( https://codingaja.tistory.com/79 ) 에서 언급했지만 소스 파일 내 여러 언어가 섞여있는 것 보다 한 가지 언어만 있는게 가장 이상적인 소스 파일이라고 했음

소스 파일 안에 JS 코드, 리엑트 코드가 혼잡하게 섞여있다면 프로젝트의 규모가 커지면서 점점 복잡해짐

 

useState 익히기 글에서 만든 컴포넌트 중 하나를 보자

import React, {useState} from 'react';

function Component7() {
    const [message, setMessage] = useState(null);

    let clickBtn = () => {
        let messageTag = document.getElementById("message");

        setMessage(messageTag.value);
    };

    let clearMessage = () => {
        let messageTag = document.getElementById("message");
        messageTag.value = "";

        setMessage("");
    };

    return(
        <div>
            <p>{message}</p>
            <hr />
            <input type="text" id="message" placeholder="메세지 입력" />
            <button type="button" onClick={clickBtn}>입력한 메세지 출력</button>
            <button type="button" onClick={clearMessage}>초기화</button>
        </div>
    );
}

export default Component7;

 

이 컴포넌트는 메세지를 입력하고 [ 입력한 메세지 출력 ] 을 클릭하면 clickBtn 함수가 호출됨

clickBtn 함수 내에서는 id가 message인 input 태그를 불러와(1) input 태그 내 입력한 값을 setter 를 사용해 저장함(2)

이때 id가 message인 input 태그를 불러올 때 JS 코드가 들어가 있음(1)

 

이렇게 태그를 불러오고 싶을 때 리엑트에서는 useRef 훅을 사용함

지금까지는 훅이 변화에 반응하는 훅이었는데 useRef 훅은 변화에 반응하지 않는, 태그를 불러오기 위해 태그를 미리 저장해두는 용도의 훅임

 

위 컴포넌트를 useRef 훅을 사용해 태그를 불러오는 컴포넌트로 바꿔보자

프로젝트 -> src -> Component20.jsx 파일을 추가 하고 아래 코드를 추가하자

import React, {useState, useRef} from 'react';

function Component7() {
    const inputRef = useRef(null);
    const [message, setMessage] = useState(null);

    let clickBtn = () => {
        setMessage(inputRef.current.value);
    };

    let clearMessage = () => {
        inputRef.current.value = "";

        setMessage("");
    };

    return(
        <div>
            <p>{message}</p>
            <hr />
            <input type="text" id="message" placeholder="메세지 입력" ref={inputRef} />
            <button type="button" onClick={clickBtn}>입력한 메세지 출력</button>
            <button type="button" onClick={clearMessage}>초기화</button>
        </div>
    );
}

export default Component7;

 

<< 코드 설명 >>

(1). useRef 훅을 사용하기 위해 import

(2). useRef 훅을 사용해 태그를 저장할 변수 선언

(3). useRef 훅을 사용해 input 태그를 inputRef 변수에 저장

  useRef 훅을 사용할 때는 태그에 ref={태그를저장할변수명} 을 사용함

(4). inputRef 변수에 저장된 input 태그에 입력한 값을 가져오거나 변화시키고 있음

 

여기서 특징적인 부분은 (4) 에서 input 태그에 접근하기 위해 inputRef.current 로 접근했다는 점임

inputRef 안에는 태그가 저장되어있는데 inputRef 안에 current 안에 저장되어있음

 

728x90
LIST

<< 학습 목표 >>

1. 컴포넌트가 마운트, 언마운트 될 때 리엑트가 컴포넌트를 어떻게 관리하는지 설명할 수 있다.

2. useEffect 훅을 사용해 컴포넌트가 언마운트 될 때 호출될 함수를 지정할 수 있다.


전 글 ( https://codingaja.tistory.com/80 ) 을 작성하다가 한가지 전혀 생각지 못핸 현상을 발견했음

 

발견한 현상을 간략히한 코드를 보자

프로젝트 -> src -> chapter03 -> Component16.jsx 를 추가하고 아래 코드를 추가하자

import React, { useState, useEffect } from 'react';

function Component16() {
    const [ click, setClick ] = useState(0);

    setInterval(function() {
        console.log("interval");
    }, 1000);

    return(
        <div>
            <button onClick={() => { setClick(click+1) }}>클릭</button>
        </div>
    );
}

export default Component16;

위 코드는 setInterval을 사용해서 1초에 한번씩 console 패널에 "interval" 을 출력하고 버튼을 클릭하면 setter 함수를 사용해 click 변수의 값을 하나씩 증가시키고 있음

 

위 컴포넌트를 출력해서 확인 해보자


위 컴포넌트를 출력하고 개발자 모드를 열어 [ console ] 패널을 보자

우선 console 패널에 "interval" 이 두 번씩 출력되는 이유는 전 글에서 설명했음

만약 왜 두 번씩 출력되는지 모르겠다면 전 글을 다시 읽어보자

 

출력된 컴포넌트에서 [ 클릭 ] 버튼을 여러 번 클릭해보자

그러면 "interval" 이 마구 출력되는걸 볼 수 있음

 

아무것도 클릭하지 않고 가만히 있을때는 1초에 두번씩 "interval" 이 출력되는데 버튼을 클릭하면 왜 여러번 출력될까?

이는 리엑트가 컴포넌트를 생성하는 방식 때문에 생기는 현상임

 

리엑트는 컴포넌트 내 변화가 생기면 기존의 컴포넌트에 변화를 주는게 아니라 기존의 컴포넌트를 버리고 변화된 새로운 컴포넌트를 만듬

 

우리가 페이지에 막 들어갔을 때 click 변수의 값이 0인 컴포넌트가 만들어지면서 setInterval이 등록됨

그리고 그 컴포넌트를 아이디가 root인 div에 출력함

 

화면에 출력된 Component16이 갖고 있는 [ 클릭 ] 버튼을 클릭하면 useState 훅이 동작해 Conponent16이 갖고 있는 click 변수의 값을 하나 증가시킴

 

 

 

 

 

 

그러나 리엑트는 현재 화면에 보여지고 있는 Component16의 click 변수의 값을 증가시키기는 방식이 아님

현재 컴포넌트(click 변수의 값이 0인 Component16)를 버리고 click 변수의 값이 1인 새로운 Component16을 만들어 현재 컴포넌트와 교체함

 

이 과정에서 기존의 컴포넌트가 등록한 setInterval이 멈추지 않고 그대로 동작하면서 새로운 컴포넌트의 setInterval도 함께 새로 등록되 setInterval이 두 번 등록되는 것

 

클릭을 한 횟수만큼 컴포넌트 교체가 이뤄지므로 그만큼 setInterval 로 등록하게 되는 것

setInterval이 많아지면 사용자의 컴퓨터가 처리해야할 setInterval의 수가 늘어나는 것이므로 사용자가 느끼는 사이트 내 속도가 굉장히 느려질 것

따라서 setInterval은 꼭 필요한 경우에만 사용해야하거나 아예 사용하면 안됨


그럼 setInterval이 필요한 상황에서는 어떻게 해야할까?

그래서 존재하는 것이 훅임

setInterval을 위해서 훅이 나온건 아니지만 setInterval을 대체해서 사용할 수 있는 훅들이 있음

 

또는~ useEffect 훅을 사용하면 컴포넌트가 마운트 됬을 때만 함수가 동작하도록 할 수 있으니 useEffect 훅을 사용하면 클릭 시 setInterval이 마구 등록되는 문제를 해결할 수 있음

이렇게 리엑트에서 setInterval을 사용해야한다면 useEffect 를 사용해서 등록하는 것이 좋음


이 문제를 더 잘 이해할 수 있는 컴포넌트들을 마지막으로 알아보자

 

프로젝트 -> src -> Component18.jsx 를 추가 하고 아래 코드를 추가하자

import React, { useEffect } from 'react';

function Component18() {
    useEffect(() => {
        setInterval(() => {
            console.log("Component18 마운트됨");
        }, 1000);
    }, []);

    return(
        <div>
            <p>안녕하세요~!</p>
        </div>
    );
}

export default Component18;

 

프로젝트 -> src -> Component17.jsx 를 추가하고 아래 코드를 추가하자

import React, {useState} from 'react';
import Component18 from './Component18';

function Component17() {
    const [isShow, setIsShow] = useState(false);
    const [btnText, setBtnText] = useState("보이기");

    let toggle = () => {
        if(isShow) {
            setBtnText("보이기");
        } else {
            setBtnText("감추기");
        }

        setIsShow(!isShow);
    }

    return(
        <div>
            <button onClick={toggle}>{btnText}</button>
            {
                isShow && <Component18 />
            }
        </div>
    );
}

export default Component17;

 

그리고 Component17을 화면에 출력하자

[ 보이기 ] 버튼이 보이는데 이 버튼을 클릭하면 Component18이 보이게됨

 

이 글의 위에서 이미 설명했는데 [ 보이기 ] 버튼을 눌렀을 때 컴포넌트가 보이는 이유는 뭘까?

잘모르겠다면 이 글을 다시 처음부터 읽어보자

리엑트를 이해하는데 굉장히 중요하고 지금까지는 당연하듯 코딩해왔던 부분을 뻥! 뚫어 줄 수 있는 내용을 설명했음

더보기

Component17은 맨 처음 아래와 같은 상황임

Component17의 isShow 변수는 false, btnText 변수는 "보이기" 를 갖고 있는 상황이므로 [ 보이기 ] 버튼이 보여지고 있는 상황이고 버튼 바로 밑으로는 조건부 렌더링을 설정해뒀는데 isShow 변수의 값이 false이므로 Component18이 안보이는 상황

 

위와 같은 상황에서 [ 보이기 ] 버튼을 클릭하면 toogle 함수가 동작하면서 btnText 변수의 값은 "감추기"로 바뀌고 isShow 변수의 값은 true 로 바뀜

 

이 글에서 컴포넌트의 변수 값이 바뀌면 어떻게 된다? 바뀐 값을 갖고 있는 새로운 컴포넌트를 만든다~!

그 후 기존의 컴포넌트는 버리고 새로운 컴포넌트로 교체한다~!

 

isShow 변수의 값은 true, btnText 변수의 값은 "감추기" 인 새 컴포넌트가 만들어지는데 이때 button 태그의 텍스트는 감추기 로 조건부 렌더링은 isShow 변수의 값이 true이므로 Component18 컴포넌트 또한 만들어지면서 Component17이 새로운 Component17로 교체됨

이때 Component18이 만들어지면서 setInterval 을 등록하기 때문에 콘솔에 1초에 한번씩 "Component18 마운트됨" 이라고 출력됨

이번에는 [ 감추기 ] 버튼을 클릭해보자

그랬을 때 어떤 현상이 발생한다? 새로운 컴포넌트로 컴포넌트 교체 현상이 발생함

 

여기까지 제대로 이해했다면 [ 감추기 ], [ 보이기 ] 버튼을 여러 번 클릭했을 때 "Component18 마운트됨" 이 여러번 출력되는게 자연스러운 현상임

 

만약 "왜? 감추기 버튼을 누르면 Component18이 사라지니까 setInterval도 사라지는거 아닌가?" 라는 생각이 들면 다시 이 글의 처음부터 보고와야함

만약 다시 처음부터 보고 왔는데도 "왜?" 라는 생각이 들면 더 여러번 다시 보고 그래도 "왜?" 라면 [ 소플의 처음 만난 리액트 ] 책을 보고 오자


 

Component17의 [ 보이기 ], [ 감추기 ] 버튼을 클릭을 계속 하다보면 "Component18 마운트 됨" 이 여러번 출력되는데 이건 어떻게 해결할 수 있을까?

더보기

Component17의 [ 보이기 ], [ 감추기 ] 버튼을 클릭을 계속 하다보면 "Component18 마운트 됨" 이 여러번 출력되는 이유 먼저 알아야함

Component18에는 setInterval을 등록하는 코드는 있지만 clearInterval 이 없어서 발생하는 문제

만약 setInterval, clearInterval을 모른다면 JS부터 먼저 공부하고 와야함

 

Component18이 만들어질 때 마다 setInterval이 등록은 되지만 Component18이 사라질 때 clearInterval을 해줘야하는 부분이 빠졌음

 

Component18이 사라질 때 clearInterval은 다음과 같이 할 수 있음

 

기존의 Component18과 바뀐 점을 비교해보면 다음과 같음

수정한 Component18에서는 setInterval로 등록하고 반환 받은 객체를 interval 변수에 저장함

그 다음 useEffect 훅 내에서 cleatInterval로 setInterval을 해제하는 함수를 반환하고 있음

 

이렇게 useEffect 훅에서 함수를 반환하면 컴포넌트가 언마운트 될 때(사라질 때) 반환한 함수가 호출됨

이를 사용해서 [ 보이기 ] 버튼을 누르면 Component18이 마운트 되며(보이며) setInterval로 등록하고 [ 감추기 ] 버튼을 누르면 Component18이 언마운트 되며(사라지며) clearInterval로 등록을 해제함


 

여기까지 useEffect 훅을 보면서 컴포넌트의 라이프 사이클에 대해서 알아봤음

setInterval 뿐만 아니라 여러 상황에서 발생하는 이해할 수 없는 문제를 해결 할 수 있는 내용이니 "setInterval을 이렇게 하는 거구나" 로 생각하지 말고 "useEffect 를 이렇게 활용하는구나" 로 넓게 생각해주기~!

728x90
LIST

<< 학습 목표 >>

1. useEffect 훅을 사용할 수 있다.

2. useeffect 훅의 세 가지 상황을 설명할 수 있다.


useEffect 훅을 배우기 전에 잠깐 우리가 알고 있는 컴포넌트에 대해서 살짝 더 안으로 들어갔다 오자

지금까지 우리는 컴포넌트를 함수 형태로 선언했음

리엑트에 처음부터 함수 형태의 컴포넌트가 있었던건 아님

리엑트에서 처음에 컴포넌트를 선언할 때는 클래스 형태로 선언해야했음

그러나 클래스 형태의 컴포넌트 선언이 불편하다는 의견이 생기면서 좀 더 간단한 방법인 함수 형태로 컴포넌트를 선언할 수 있는 방법이 생긴 것

 

우리가 훅을 배우다 갑자기 컴포넌트의 형태에 대해서 얘기하는 이유는 우리가 지금 배우고 있는 훅은 함수 형태 컴포넌트를 위해 나온 것임

따라서 훅은 리엑트에 처음부터 있었던게 아님

리엑트에 클래스 형태 컴포넌트만 있었을 때 클래스 형태 컴포넌트가 사용하던 훅이 있음

<< 클래스 형태 컴포넌트가 사용하는 훅 >>

- componentDidMount() : 컴포넌트가 화면에 출력된 이후 호출되는 메서드

- componentDidUpdate() : 컴포넌트에 어떤 변화가 생길 때마다 변화가 생긴 이후에 호츨되는 메서드

- componentWillUInmount() : 컴포넌트가 화면에서 사라질 때 사라지기 직전에 호출되는 메서드

 

전 글에서 배운 useState 훅은 componentDidUpdate() 메서드와 같은 역할을 하는 훅임

이번에 배울 useEffect 훅은 componentDidMount() 메서드와 같은 역할을 할 수 있고 componentDidUpdate() 메서드와 같은 역할을 할 수 있고 또 componentWillUInmount() 와 같은 역할을 할 수 있는 훅임

 

즉, 이번에 배울 useEffect 훅은 컴포넌트의 다양한 상태에 반응하는 훅임


useEffect 훅의 형식은 다음과 같음

useEffect(함수, 배열);

 

useEffect는 배열 자리에 변수들을 갖고 있는 배열을 넣음

그리고 배열 안에 들어있는 변수들 중 어느 하나라도 값이 변하면 함수를 호출함

 

배열 자리에 변수들을 갖고 있는 배열을 반드시 넣어야하는건 아님

1. 배열 자리에 변수들을 갖고 있는 배열을 넣지 않을 수도 있고 

2. 배열 자리에 변수들을 갖고 있는 배열을 넣을 수도 있고

3. 배열 자리에 빈 배열을 넣을 수도 있음


<< 1. 배열 자리에 변수들을 갖고 있는 배열을 넣지 않기 >>

배열 자리에 변수들을 갖고 있는 배열을 넣지 않기

즉, 두 번째 인자를 아예 넣지 말자 

두 번째 인자를 아예 넣지 않으면 componentDidMount(), componentDidUpdate() 와 같은 동작을 하는 훅이 됨

 

프로젝트 -> src -> chapter03 -> Component13.jsx 를 추가하고 아래 코드를 추가하자

import React, { useState, useEffect } from 'react';

function Component13() {
    const [count, setCount] = useState(0);

    let clickBtn = () => {
        setCount(count+1);
    }

    let updatedPage = () => {
        console.log("페이지에 변화가 생겼습니다.");
    }

    useEffect(updatedPage);

    return(
        <div>
            <p>총 {count}번 클릭했습니다</p>
            <button onClick={clickBtn}>클릭~!</button>
        </div>
    );
}

export default Component13;

 

버튼을 클릭하면 useState 훅이 동작해 count 변수의 값을 증가(1)시키고 증가된 값을 출력함(2)

useEffect 훅을 사용해서 페이지에 어떤 변화가 생길 때 마다 이를 감지하기 위해 두 번째 인자는 비워뒀음(3)

페이지에 어떤 변화가 생길 때 마다 updatePage 메서드가 호출됨(4)

여기서 어떤 변화란 페이지 내 모든, 말 그대로 어떤것이든 변화를 뜻함

이 페이지에는 count 변수의 값을 증가시키고 count 변수의 값을 출력하는 변화가 발생함

 

이 컴포넌트를 웹 페이지에 출력하고 웹 페이지에서 개발자 도구를 열어 [ console 패널 ] (1) 을 확인해보자

코드 설명대로 결과를 보기 전에~!

 

나는 페이지에 막 들어오기만 했지 아무것도 누르지 않았음

그런데 개발자 도구를 보면 "페이지에 변화가 생겼습니다." 가 두 번 출력됬음

 

버튼을 클릭하지도 않았는데 왜 이렇게 출력된걸까?

리엑트가 컴포넌트를 화면에 출력하는 구조를 생각해보면 맨 처음에는 아이디가 root인 div 태그가 있었고 그 태그에 컴포넌트를 출력하는 것이었음

아이디가 root인 div 태그에 컴포넌트가 출력되는 변화가 생겼으므로 아무것도 클릭하지 않아도 "페이지에 변화가 생겼습니다." 가 출력됨

 

두 번 출력되는 이유는 뭘까??

두 번 출력된 이유는 리엑트가 자체적으로 테스트를 하기 때문임

개발자의 실수로 컴포넌트 내 코드에 어떤 문제가 생겼을 수 있으므로 사용자에게 화면을 보여주기 전 리엑트 자체적으로 컴포넌트를 화면에 출력해봄

개발자의 실수로 컴포넌트 내 코드에 문제가 생겼다면 컴포넌트가 보이지 않고 오류 페이지가 보임

개발자가 실수 하지 않아 컴포넌트 내 코드에 문제가 없다면 사용자에게 컴포넌트를 보여주기 위해 컴포넌트를 출력함

 

그래서 "리엑트가 컴포넌트를 테스트 하기 위해 컴포넌트를 출력한다" 라는 변화가 생겼으므로 첫 번째로 "페이지에 변화가 생겼습니다." 가 출력되고 컴포넌트가 테스트에 통과해 컴포넌트를 출력하므로 두 번째 "페이지에 변화가 생겼습니다." 가 출력되는 것

 

이제 버튼을 클릭해보면 [ console 패널 ] 에 "페이지에 변화가 생겼습니다." 가 출력될 것

 

아래와 같이 페이지에 변화를 주는 요소가 늘어나면 변화 마다 useEffect로 지정한 함수가 호출되므로 useEffect로 지정한 함수가 더 많이 호출될 것


<< 2. 배열 자리에 변수들을 갖고 있는 배열을 넣기 >>

useEffect 훅의 두 번째 인자(배열 자리)에 변수들을 갖고 있는 배열을 넣으면 배열 내 변수 중 어느 하나라도 변수의 값이 변하면 useEffect 로 지정한 함수가 호출됨

 

프로젝트 -> src -> chapter03 -> Component14.jsx 파일을 추가하고 아래 코드를 추가하자

import React, { useState, useEffect } from 'react';

function Component14() {
    const [count1, setCount1] = useState(0);
    const [count2, setCount2] = useState(0);

    let clickBtn1 = () => {
        setCount1(count1+1);
    }

    let clickBtn2 = () => {
        setCount2(count2+1);
    }

    let updatedPage = () => {
        console.log("count1변수에 변화가 생겼습니다.");
    }

    useEffect(updatedPage, [count1]);

    return(
        <div>
            <h1>count1</h1>
            <p>총 {count1}번 클릭했습니다</p>
            <button onClick={clickBtn1}>클릭~!</button>

            <hr />

            <h1>count2</h1>
            <p>총 {count2}번 클릭했습니다</p>
            <button onClick={clickBtn2}>클릭~!</button>
        </div>
    );
}

export default Component14;

 

이번 컴포넌트에는 버튼을 클릭했을 때 변하는 변수가 2개임

useEffect 훅으로 변화를 감지할 변수로 count1 하나만 등록해뒀으므로 첫 번째 버튼을 클릭하면 [ console 패널 ] 에 "count1변수에 변화가 생겼습니다" 가 출력됨

두 번째 버튼을 클릭했을 때는 아무것도 출력되지 않음

 

useEffect 훅이 변화를 감지할 변수를 이와 같이 여러 개로 등록을 해두면 배열 내 변수 중 하나라도 변수의 값이 변했을 때 useEffect 훅으로 지정한 함수가 호출됨


<< 3. 배열 자리에 빈 배열을 넣기 >>

useEffect 훅의 두 번째 인자(배열 자리)에 빈 배열을 넣으면 componentDidMount(), componentWillUInmount() 시점에 useEffect 로 지정한 함수가 호출됨

즉, 페이지에 막 들어왔을 때( componentDidMount ), 페이지에서 해당 컴포넌트가 사라질 때 ( componentWillUInmount ) useEffect 로 지정한 함수가 호출됨

 

바로 위에서 만들었던 Component14 컴포넌트를 약간 수정해 빈 배열을 넣어보자

빈 배열이므로 useEffect 훅이 변화를 감지할 변수가 없는 것 그래서 페이지에 막 들어왔을 떄와 페이지에서 해당 컴포넌트가 사라질 때만 useEffect 훅으로 등록한 함수가 호출됨


여기까지 useState 훅 다음으로 자주 사용하는 useEffect 훅을 배웠음

useEffect 훅의 두 번째 인자로 뭘 넣느냐에 따라서 useEffect 훅으로 등록한 함수가 호출되는 시점이나 횟수가 매우 상이하니 잘 정리해둬야함

728x90
LIST

<< 학습 목표 >>

1. useState 훅이 필요한 상황을 판단할 수 있다.

2. 조건부 렌더링을 구현할 수 있다.


이번에는 전 글 ( https://codingaja.tistory.com/78 ) 에서 배운 useState 훅을 좀 더 익혀보자

 

프로젝트 -> src -> chapter03 -> Component7.jsx 파일을 추가하고 아래 이미지를 참고해

1. 메세지를 입력한 후 [ 입력한 메세지 출력 ] 버튼을 클릭하면 수평선 위에 입력한 메세지를 출력할 것

2. [ 초기화 ] 버튼을 클릭하면 수평선 위에 출력된 메세지, input 태그 안에 입력한 메세지가 사라질 것

을 구현해보자

 

시간이 오래걸리더라도 힌트를 보지 않고 직접 구현하는게 본인의 실력을 키우고 리엑트를 학습할 수 있는 방법이니 최소 10분은 직접 고민한 후 힌트를 보자

 

만약 힌트를 본다면 각 힌트 내 코드를 단순히 따라 입력하지 말자

따라 입력하는건 타자연습임

타자연습을 하기 위해 시간들여 이 글을 읽는 분은 없을 것

각 힌트를 보고 이해 한 후 입력 하자

 

<< 1단계 힌트 >>

더보기

import React from 'react';

function Component7() {
    return(
        <div>
            <p></p>
            <hr />
            <input type="text" id="message" placeholder="메세지 입력" />
            <button type="button">입력한 메세지 출력</button>
            <button type="button">초기화</button>
        </div>
    );
}

export default Component7;

<< 2단계 힌트 >>

더보기

import React from 'react';

function Component7() {
    return(
        <div>
            <p></p>
            <hr />
            <input type="text" id="message" placeholder="메세지 입력" />
            <button type="button" onClick={clickBtn}>입력한 메세지 출력</button>
            <button type="button" onClick={clearMessage}>초기화</button>
        </div>
    );
}

export default Component7;

<< 3단계 힌트 >>

더보기

import React, {useState} from 'react';

function Component7() {
    const [message, setMessage] = useState(null);

    let clickBtn = () => {

    };

    let clearMessage = () => {

    };

    return(
        <div>
            <p>{message}</p>
            <hr />
            <input type="text" id="message" placeholder="메세지 입력" />
            <button type="button" onClick={clickBtn}>입력한 메세지 출력</button>
            <button type="button" onClick={clearMessage}>초기화</button>
        </div>
    );
}

export default Component7;

<< 4단계 힌트 >>

더보기

import React, {useState} from 'react';

function Component7() {
    const [message, setMessage] = useState(null);

    let clickBtn = () => {
        let messageTag = document.getElementById("message");

        setMessage(messageTag.value);
    };

    let clearMessage = () => {

    };

    return(
        <div>
            <p>{message}</p>
            <hr />
            <input type="text" id="message" placeholder="메세지 입력" />
            <button type="button" onClick={clickBtn}>입력한 메세지 출력</button>
            <button type="button" onClick={clearMessage}>초기화</button>
        </div>
    );
}

export default Component7;

<< 최종(5단계) 힌트 >>

더보기

import React, {useState} from 'react';

function Component7() {
    const [message, setMessage] = useState(null);

    let clickBtn = () => {
        let messageTag = document.getElementById("message");

        setMessage(messageTag.value);
    };

    let clearMessage = () => {
        let messageTag = document.getElementById("message");
        messageTag.value = "";

        setMessage("");
    };

    return(
        <div>
            <p>{message}</p>
            <hr />
            <input type="text" id="message" placeholder="메세지 입력" />
            <button type="button" onClick={clickBtn}>입력한 메세지 출력</button>
            <button type="button" onClick={clearMessage}>초기화</button>
        </div>
    );
}

export default Component7;

 

<< 코드 설명 >>

더보기

(1). 위에서는 useState 훅을 따로 import 했지만 React, useState 모두 react 에서 import 해오므로 이와 같이 합칠 수 있음

(2). 화면에 입력한 메세지를 저장하고 출력하기 위해 useState 훅을 사용해 message 변수 선언

(3). [ 입력한 메세지 출력 ] 버튼을 클릭 했을 때 호출되는 함수로

  아이디가 message인 input 태그를 불러와 사용자가 입력한 값을 message 변수에 저장

(4). [ 초기화 ] 버튼을 버튼을 클릭 했을 때 호출되는 함수로

  아이디가 message인 input 태그를 불러와 사용자가 입력한 값을 지움

  message 변수에 저장된 값을 지움

(5). message 변수에 저장된 값을 화면에 출력



프로젝트 -> src -> chapter03 -> Component8.jsx 파일을 추가하고 아래 이미지를 참고해

1. 메세지를 입력하면 동시에 수평선 위에 입력한 메세지가 출력되도록 할 것

2. [ 초기화 ] 버튼을 클릭하면 수평선 위에 출력된 메세지, input 태그 안에 입력한 메세지가 사라질 것

을 구현해보자

 

 

<< 1단계 힌트 >>

더보기

import React from 'react';

function Component8() {
    return(
        <div>
            <p></p>
            <hr />
            <input type="text" id="message" placeholder="메세지 입력" />
            <button type="button">초기화</button>
        </div>
    );
}

export default Component8;

<< 2단계 힌트 >>

더보기

import React from 'react';

function Component8() {
    return(
        <div>
            <p></p>
            <hr />
            <input type="text" id="message" placeholder="메세지 입력" onChange={changeMessage} />
            <button type="button" onClick={clearMessage}>초기화</button>
        </div>
    );
}

export default Component8;

<< 3단계 힌트 >>

더보기

import React, {useState} from 'react';

function Component8() {
    const [message, setMessage] = useState(null);

    let changeMessage = (event) => {

    }

    let clearMessage = () => {

    };

    return(
        <div>
            <p></p>
            <hr />
            <input type="text" id="message" placeholder="메세지 입력" onChange={changeMessage} />
            <button type="button" onClick={clearMessage}>초기화</button>
        </div>
    );
}

export default Component8;

<< 4단계 힌트 >>

더보기

import React, {useState} from 'react';

function Component8() {
    const [message, setMessage] = useState(null);

    let changeMessage = (event) => {

    }

    let clearMessage = () => {
        let messageTag = document.getElementById("message");
        messageTag.value = "";

        setMessage("");
    };

    return(
        <div>
            <p></p>
            <hr />
            <input type="text" id="message" placeholder="메세지 입력" onChange={changeMessage} />
            <button type="button" onClick={clearMessage}>초기화</button>
        </div>
    );
}

export default Component8;

<< 최종(5단계) 힌트 >>

더보기

import React, {useState} from 'react';

function Component8() {
    const [message, setMessage] = useState(null);

    let changeMessage = (event) => {
        setMessage(event.target.value);
    }

    let clearMessage = () => {
        let messageTag = document.getElementById("message");
        messageTag.value = "";

        setMessage("");
    };

    return(
        <div>
            <p>{message}</p>
            <hr />
            <input type="text" id="message" placeholder="메세지 입력" onChange={changeMessage} />
            <button type="button" onClick={clearMessage}>초기화</button>
        </div>
    );
}

export default Component8;

<< 코드 설명 >>

더보기

(1). input 태그에 값을 입력할 때 마다 changeMessage 함수 호출

(2). 함수가 호출될 때 setter 를 사용해 입력한 값을 message 변수에 저장

(3). message 변수에 저장된 값 출력



전 글 ( https://codingaja.tistory.com/77 ) 에서 마지막으로 만들었던 Component5 컴포넌트 코드를 useState 훅을 사용해 변경하자

Component5 컴포넌트의 어디에 useState 훅을 사용해야할까?

import React from 'react';

function Component5() {
    let changeUrl = (event) => {
        changeATag(event.target.value);
    };

    let changeATag = (url) => {
        let aTag = document.getElementById("anchor");
        aTag.setAttribute("href", url);
        aTag.textContent = url + " 으로 이동~!";
    }

    return(
        <div>
            <input type="url" placeholder="이동하고 싶은 URL을 입력하세요." onChange={changeUrl} />
            <br />
            <a href="#" id="anchor"></a>
        </div>
    );
}

export default Component5;

 

반드시 아래 코드를 보기 전에 직접 해결해볼 것

만약 어디에 어떻게 적용해야할 지 모르겠다면 전 글을 보고 useState 훅 기초를 다시 다지고 오자

 

<< useState 훅을 사용한 Component5 컴포넌트 >>

더보기

import React, {useState} from 'react';

function Component5() {
    const [url, setUrl] = useState(null);

    let changeUrl = (event) => {
        setUrl(event.target.value);
    };

    return(
        <div>
            <input type="url" placeholder="이동하고 싶은 URL을 입력하세요." onChange={changeUrl} />
            <br />
            <a href={url} id="anchor">{url} 로 이동~!</a>
        </div>
    );
}

export default Component5;

 

<< 코드 설명 >>

더보기

useState 훅을 사용하지 않은 컴포넌트와 useState 훅을 사용한 컴포넌트를 비교해보면 useState 훅의 용도를 다시 상기할 수 있을 것

 

전 글에서 useState 훅은 화면에 출력할 값을 등록하고 변화시키는 훅이라고 했음

input 태그에 url을 입력할 때마다 a 태그의 href 속성과 text가 변해야하므로 이를 위해 useState 훅을 사용하고 변한 url을 출력하는 것



이번에는 select 태그로 선택한 값을 화면에 출력해보자

프로젝트 -> src -> chapter03 -> Component9.jsx 파일을 추가하고 아래 이미지를 참고해

1. 지역을 선택하면 선택한 지역명을 출력되도록 할 것

을 해보자

도시는 서울특별시, 인천광역시, 경기도, 구는 북구, 남구, 서구, 동구 로 통일해서 만들자

 

<< 컴포넌트 코드 >>

더보기

import React, { useState } from 'react';

function Component9() {
    const [region, setRegion] = useState(null);
    const [city, setCity] = useState(null);

    let changeRegion = (event) => {
        setRegion(event.target.value);
    }

    let changeCity = (event) => {
        setCity(event.target.value);
    }

    return(
        <div>
            <p>{region} {city} 에 사시는군요~!</p>
            <select name="region" id="region" onChange={changeRegion}>
                <option value="">사는 도시를 선택하세요.</option>
                <option value="서울특별시">서울특별시</option>
                <option value="인천광역시">인천광역시</option>
                <option value="경기도">경기도</option>
            </select>
            <select name="city" id="city" onChange={changeCity}>
                <option value="">사는 구를 선택하세요.</option>
                <option value="북구">북구</option>
                <option value="남구">남구</option>
                <option value="서구">서구</option>
                <option value="동구">동구</option>
            </select>
        </div>
    );
}

export default Component9;

 

useState는 컴포넌트 안에서 필요에 따라 여러번 사용할 수 있음

이쯤 되면 이런 코드는 간단한 코드일테니 별도의 설명은 없음



이번에는 문제가 아닌 같이 알아보자

다음과 같이 성별을 선택하고 출력하는 페이지를 보자

프로젝트 -> src -> chapter03 -> Component10.jsx 파일을 추가하고 아래 코드를 추가하자

import React, { useState } from 'react';

function Component10() {
    const [gender, setGender] = useState(null);

    let selectedGender = (event) => {
        setGender(event.target.value);
    }

    return(
        <div>
            <p>성별 :
                <input type="radio" value="남성" name="gender" onChange={selectedGender} />남성
                <input type="radio" value="여성" name="gender" onChange={selectedGender} />여성
            </p>
            <hr />
            <p>{gender} 이시군요~!</p>
        </div>
    );
}

export default Component10;

 

지금까지 봐온 코드와 다를바 없기 때문에 이제는 아주 쉬운 코드가 됐을 것

이 컴포넌트를 웹 페이지에 출력해 확인해보자

 

 

이 페이지에서 보기 안좋은 부분이 있는데

페이지에 막 들어왔을 때는

아직 성별을 선택하기 전이므로 "이시군요~!" 만 출력됨

 

 

성별을 선택하기 전이라면 아무것도 출력되지 않고 성별을 선택했을 때에만 "남성 이시군요~!" 또는 "여성 이시군요~!" 가 출력되도록 변경해보자

 

Component10 컴포넌트의 p 태그 부분을 변경 후 로 바꾸면 됨

변경 후의 p 태그를 출력하는 코드 부분을 조건부 렌더링이라고 부름

 

조건부 렌더링을 할 때 if 조건부 렌더링이 있고 if ~ else 조건부 렌더링이 있음

우리가 지금 사용한 조건부 렌더링은 if 조건부 렌더링임

if 조건부 렌더링의 형식 -> 조건식 && 태그

 

if문은 조건식이 참이면 if문 안에 있는 코드가 실행되고 조건식이 거짓이면 if문 안에 있는 코드가 실행되지 않음

if 조건부 렌더링도 이와 같이 동작함

if 조건부 렌더링의 조건식이 참이면 태그를 출력(Rendering/렌더링)하고 조건식이 거짓이면 태그를 출력하지 않는 것

 

useState 훅을 사용해서 gender 변수를 선언하고 gender 변수를 null 로 만들었음(1)

페이지에 막 들어왔을 때 조건부 렌더링(2)이 동작하는데 조건식이 false, 거짓,이므로(3) 태그가 출력되지 않음

성별을 선택하면 gender 변수의 값이 바뀌면서 다시 조건부 렌더링이 동작하고 조건식이 true, 참,이므로 태그가 출력되면서 "(성별) 이시군요~!" 가 출력됨

Component10 컴포넌트를 웹 페이지에 출력해 결과를 확인해보자

 

이번에는 if ~ else 조건부 렌더링을 알아보자

if ~ else 조건부 렌더링의 형식 -> 조건식 ? 태그 A : 태그 B

 

if ~ else문은 조건식이 참이면 if문의 코드가 실행되고 조건식이 거짓이면 else문의 코드가 실행

if ~ else 조건부 렌더링도 이와 같음

if ~ else 조건부 렌더링의 조건식이 참이면 태그 A를 출력하고 조건식이 거짓이면 태그 B를 출력하는 것

 

if ~ else 조건부 렌더링을 사용해보자

Component10.jsx 파일을 복사 붙여넣기 하고 이름을 Component11.jsx 로 바꾸자

그리고 Component11.jsx 파일 내 컴포넌트명을 Conponent11 로 바꾸자

 

그 후 if 조건부 렌더링 부분을 아래와 같이 바꾸자

import React, { useState } from 'react';

function Component11() {
    const [gender, setGender] = useState(null);

    let selectedGender = (event) => {
        setGender(event.target.value);
    }

    return(
        <div>
            <p>성별 :
                <input type="radio" value="남성" name="gender" onChange={selectedGender} />남성
                <input type="radio" value="여성" name="gender" onChange={selectedGender} />여성
            </p>
            <hr />
            { gender == null ? <p>성별을 선택하세요~!</p> : <p>  {gender} 이시군요~!</p> }
        </div>
    );
}

export default Component11;

 

페이지에 막 들어왔을 때는 if ~ else 조건부 렌더링의 조건식이 참이므로 "성별을 선택하세요~!" 가 출력됨

성별을 선택하면 gender 변수의 값이 바뀌면서 조건부 렌더링이 다시 동작함

이때 조건부 렌더링의 조건식이 거짓이므로 "(성별) 이시군요~!" 가 출력됨

 

조건부 렌더링을 할 때는 꼭 if 조건부 렌더링, if ~ else 조건부 렌더링 방식으로만 해야할까?

아님

아래의 Panel 컴포넌트와 같이 컴포넌트를 만들어 상황에 맞는 태그를 출력(렌더링)하도록 할 수 있음

Panel 컴포넌트 안에서 useState 훅으로 선언한 gender 변수를 사용했기 때문에 gender 변수의 값이 바뀔 때 마다 Panel 컴포넌트가 호출되면서 상황에 맞는 p 태그가 출력됨

import React, { useState } from 'react';

function Component12() {
    const [gender, setGender] = useState(null);

    let selectedGender = (event) => {
        setGender(event.target.value);
    }

    let Panel = () => {
        if(gender == null) {
            return <p>성별을 선택하세요~!</p>;
        } else {
            return <p> {gender} 이시군요~!</p>
        }
    }

    return(
        <div>
            <p>성별 :
                <input type="radio" value="남성" name="gender" onChange={selectedGender} />남성
                <input type="radio" value="여성" name="gender" onChange={selectedGender} />여성
            </p>
            <hr />
            {Panel()}
        </div>
    );
}

export default Component12;

 

여기까지 useState 훅을 익히고 조건부 렌더링을 배웠음

useState 훅, 조건부 렌더링은 굉장히 자주 사용하므로 잘 기억해두자

728x90
LIST

<< 학습 목표 >>

1. useState 훅을 사용해야하는 이유를 설명할 수 있다.

2. setter 를 사용해 usState훅으로 선언한 변수의 값을 변화시킬 수 있다.

3. useState 훅으로 선언한 변수에 저장된 값을 화면에 출력할 수 있다.


전 글 ( https://codingaja.tistory.com/77 ) 에서 마지막으로 만들었던 Component5 컴포넌트 코드를 보자

import React from 'react';

function Component5() {
    let changeUrl = (event) => {
        changeATag(event.target.value);
    };

    let changeATag = (url) => {
        let aTag = document.getElementById("anchor");
        aTag.setAttribute("href", url);
        aTag.textContent = url + " 으로 이동~!";
    }

    return(
        <div>
            <input type="url" placeholder="이동하고 싶은 URL을 입력하세요." onChange={changeUrl} />
            <br />
            <a href="#" id="anchor"></a>
        </div>
    );
}

export default Component5;

이 컴포넌트에서 화면에 출력할 값은 사용자가 입력한 URL로 changeUrl 함수를 거쳐 changeATag 함수로 전달되고 있음

이렇게 화면에 출력할 값이 변할 때는 반드시 훅(Hook)으로 관리를 해야함

 

훅은 다음과 같은 것들이 있음

- useState

- useEffect

- useContext

- useReducer

- useCallback

- useMemo

- useRef

 

이 훅 모두를 반드시 알아야하는건 아니고 useState, useEffect 훅은 반드시 알아야하고 나머지 훅들은 "이런게 있다" 정도로만 기억하면 됨

 

이번 글에서는 가장 많이 사용하는 useState 하나만 알아보자


<< useState 훅 >>

useState 훅은 출력할 값을 등록하고 변화시킬 때 사용하는 훅임

 

useState 훅을 사용하려면 import { useState ] from 'react'; 로 불러와야함

useState 훅을 불러왔다면 리엑트 코드 내에서 const [ 변수명, set변수명 ] = useState(초깃값); 으로 사용함

 

useState를 사용하지 않은 컴포넌트를 보고 그 컴포넌트에서 useState를 사용해야하는 이유와 useState를 사용한 컴포넌트로 바꿔보자

1. useState를 사용하지 않은 컴포넌트

프로젝트 -> src -> chapter03 -> Component6.jsx 파일을 추가하고 아래 코드를 추가하자

import React from 'react';

function Component6() {
    let count = 0;

    let clickBtn = () => {
        count++;

        let pTag = document.getElementById("panel");
        pTag.textContent = "버튼을 " + count + "번 클릭했습니다";
    };

    return(
        <div>
            <p id="panel">버튼을 {count} 번 클릭했습니다</p>
            <button type="button" onClick={clickBtn}>클릭</button>
        </div>
    );
}

export default Component6;

 

<< 코드 설명 >>

(1). "버튼을 n번 클릭했습니다" 를 출력 할 태그

  먼저 "버튼을 0번 클릭했습니다" 를 출력하기 위해 초기 텍스트를 갖고 있음

(2). 버튼을 클릭할 때 마다 (4)의 함수를 호출

(3). 버튼을 몇 번 클릭했는지 저장하기 위한 변수

(4). 버튼 클릭 횟수를 증가시키고 "p 태그에 버튼을 n번 클릭했습니다" 를 출력

 

2. 이 컴포넌트에서 useState 를 사용해야하는 이유

  count 변수는 화면에 출력할 값을 갖고 있는 변수임

  useState 훅을 사용하지 않아도 이와 같이 구현할 수 있지만 useState의 시작에서 언급했던것 처럼 리엑트를 사용해서 개발한다면 count 변수는 반드시 useState 훅을 사용해 선언해야하고 count 변수의 값을 증가시킬 때도 useState 훅을 사용해 증가시켜야함

 

3. useState 훅을 사용한 컴포넌트

Component6.jsx의 코드를 아래와 같이 바꾸자

import React from 'react';
import {useState} from 'react';

function Component6() {
    const [count, setCount] = useState(0);

    let clickBtn = () => {
        setCount(count+1);
    };

    return(
        <div>
            <p id="panel">버튼을 {count} 번 클릭했습니다</p>
            <button type="button" onClick={clickBtn}>클릭</button>
        </div>
    );
}

export default Component6;

 

<< 코드 설명 >>

(1). useState 훅을 사용하기 위한 import

(2). useState 훅을 사용해 count 변수를 선언하고 count 변수에 0을 저장함 

  또한 "count변수의 값을 변화시킬 때는 setCount 라는 이름의 함수를 사용하겠다" 는 것

(3). "버튼을 n번 클릭했습니다." 를 출력할 태그

(4). 버튼을 클릭할 때 마다 (5)의 함수를 호출함

(5). 함수가 호출될 때 마다 useState 훅을 사용해 등록해준 setCoun 함수를 호출함

  setCount 함수를 호출해 count변수의 값을 하나씩 증가시키고 있음

 

useState를 사용해야하는 컴포넌트에서 useState를 사용하지 않았을 때와 사용했을 때 코드를 비교해보자

useState를 사용하지 않아도 똑같은 역할의 컴포넌트를 구현할 수 있음

useState를 사용했을 때 가장 눈에 띄는 변화는 코드 수가 감소했다는 것

 

또한 리엑트 코드에서 JS 코드를 최대한 뺄 수 있음

이것 저것 왔다 갔다 하면 개발 할 때 힘듬

소스 파일에 JS 코드만 있거나 리엑트 코드만 있는 상황이 가장 이상적인 상황일 것

왜? 이것 저것 생각할 필요 없이 JS 나 리엑트만 생각하면 되므로

이렇게 useState 훅을 사용해서 화면에 출력할 값을 관리하면 JS 코드를 최대한 생략할 수 있으므로 가독성 높은, 개발하기 편한 코드가 됨

 

useState 훅을 사용한 코드를 좀 더 자세히 설명해보자
(1). count 변수를 선언

(2). count 변수의 값을 변화시킬 때 사용할 함수

  이 함수의 이름은 일반적으로 set으로 시작하고 뒤에 변수이름이 붙음

  그래서 이 함수를 setter 함수라고 부름

  count 변수의 값을 변화시킬 때는 반드시 setter 함수를 사용해야함

(3). count변수의 초기값

  (1), (3)을 합쳐 JS 코드로 바꿔보면 let count = 0; 과 같음

(4). setter 함수를 사용해 count 변수의 값을 하나씩 증가시키고 있음

(5). count 변수의 값을 출력함

  count 변수의 값이 변하면 변한 count 변수의 값을 출력함

 

아래와 같이 (4)에서 setter 함수를 사용하지 않고 직접 count 변수의 값을 변화시키려고 하면 문제가 생김

 

여기서 많이 하는 실수가 아래와 같이 setter 함수를 사용하긴 했지만 증감 연산자를 사용하는 실수를 함

증감 연산자를 사용해도 당연히 문제가 생김

만약 "어 왜?" 라는 생각이 든다거나 증감 연산자를 잘모른다면 반드시 리엑트를 잠시 멈추고 연산자 파트 전체를 공부하고 와야함

 

setter 함수는 반드시 함수 안에서만 호출해야함

아래와 같이 함수 바깥에서 호출(1)하면 문제가 생김

728x90
LIST

<< 학습 목표 >>

1. 이벤트 핸들러를 리엑트 방식으로 등록할 수 있다.

2. 이벤트 핸들러로 적절한 인자를 전달할 수 있다.


이번 글에서 배우는 내용은 리엑트 라기 보다는 JS의 함수에 대한 내용으로 만약 이해되지 않는 부분이 있거나 "오~ 이렇게 되네?" 라는 생각이 들었다면 JS의 함수를 꼭 공부하고 와야함

 

우선 프로젝트 -> src -> chapter03 폴더를 추가하자


HTML, JS, 리엑트의 이벤트 등록 방식을 비교해보자

 

HTML에서는 이벤트를 등록할 때 on으로 시작하는 이벤트명을 태그에 사용해 이벤트 핸들러를 등록함

JS는 이벤트를 등록할 태그를 불러온 뒤 addEventListener 함수를 사용해 이벤트 핸들러를 등록함

리엑트는 on으로 시작하는 이벤트명을 태그에 사용해 이벤트 핸들러를 등록함

 

HTML과 리엑트, JS와 리엑트를 비교해보면 HTML, JS가 모두 섞인 방식이 리엑트임

리엑트에서 이벤트를 등록할 때는 태그에 on으로 시작하는 이벤트 명을 태그에 사용함

등록할 이벤트명은 () 없이 이벤트 핸들러 이름만 명시함


리엑트에서 이벤트 핸들러를 위와 같이 등록할 수도 있지만 간단한 이벤트 핸들러의 경우 생성하면서 등록할 수도 있음

 

버튼을 클릭 했을 때 웹 페이지에 "클릭했음!" 을 출력하는 컴포넌트를 만들어보자

프로젝트 -> src -> chapter03 -> Component1.jsx 파일을 추가하고 아래 코드를 추가하자

import React from 'react';

function Component1() {
    return(
        <div>
            <button type="button" onClick={function() { alert("클릭했음!"); }}>클릭!</button>
        </div>
    );
}

export default Component1;

 

이렇게 간단한 이벤트 핸들러의 경우 생성하면서 등록할 수 있음

당연히 JS의 화살표 함수를 사용해 생성하면서 등록할 수도 있음


한번 더 함수를 활용하는 방법을 보자

 

프로젝트 -> src -> Component3.jsx 파일을 추가하고 아래 코드를 추가하자

import React from 'react';

function Component3() {
    let printName = () => {
        let nameTag = document.getElementById("name");
        let panelTag = document.getElementById("panel");

        panelTag.textContent = "사용자가 입력한 이름은 " + nameTag.value + " 입니다";
    };

    return(
        <div>
            <input type="text" id="name" placeholder="이름"></input>
            <p id="panel"></p>
            <button type="button" onClick={printName}>이름 출력</button>
        </div>
    );
}

export default Component3;

 

<< 코드 설명 >>

(1). 사용자가 이름을 입력할 수 있는 input 태그

(2). 사용자가 입력한 이름을 출력할 p 태그

(3). 버튼을 누르면 (4)의 함수를 호출함

(4). 함수가 호츨되면

  (4-1). id가 name인 태그를 불러옴

  (4-2). id가 panel인 태그를 불러옴

  (4-3). id가 panel인 태그에 사용자가 입력한 이름을 출력함


이번에는 함수의 매개변수로 값을 전달하는 방법을 보자

이 역시 JS이므로 우리가 알고 있는 방식 그대로 다 적용 가능함

 

프로젝트 -> src -> chapter03 -> Component4.jsx 를 추가하고 아래 코드를 추가하자

import React from 'react';

function Component4() {
    let printName = (name) => {
        let namePanelTag = document.getElementById("name_panel");
        namePanelTag.textContent = name;
    };

    let clickBtn = () => {
        printName("홍길동");
    }

    return(
        <div>
            <p id="name_panel"></p>
            <button type="button" onClick={clickBtn}>이름 출력</button>
        </div>
    );
}

export default Component4;


<< 코드 설명 >>

(1). 이름을 출력할 p 태그

(2). 버튼을 누르면 (3)의 함수를 호출함

(3). 함수가 호출되면 (4)의 함수를 호출하는데 함수로 "홍길동" 이름을 전달

(4). 전달 받은 이름을 (1)의 p 태그에 출력

 

이 컴포넌트를 웹 페이지에 출력해 결과를 확인해보자

 

 

이렇게 어떤 함수로 인자를 전달하려면 (3)처럼 중간 다리 역할을 할 함수가 필요함

만약 이를 아래와 같이 구현한다면 이 페이지에는 문제가 생겨 화면에 아무것도 보이지 않음

위 코드와 아래 코드를 비교해 무엇이 다른지, 아래 코드는 왜 문제가 생기는지 생각해보자

만약 이유를 모르겠다면 반드시 리엑트의 JSX 로 돌아가서 공부하고 와야함

 

이번에는 함수로 이벤트(event)를 전달하는 방법을 보자

이 역시도 우리가 이미 알고 있는 JS 방식과 동일함

import React from 'react';

function Component5() {
    let changeUrl = (event) => {
        changeATag(event.target.value);
    };

    let changeATag = (url) => {
        let aTag = document.getElementById("anchor");
        aTag.setAttribute("href", url);
        aTag.textContent = url + " 으로 이동~!";
    }

    return(
        <div>
            <input type="url" placeholder="이동하고 싶은 URL을 입력하세요." onChange={changeUrl} />
            <br />
            <a href="#" id="anchor"></a>
        </div>
    );
}

export default Component5;

 

<< 코드 설명 >>

(1). 사용자가 url을 입력할 수 있는 input 태그 / input 태그에 url을 입력하면 (3)의 함수가 호출됨

(2). 사용자가 입력한 url로 이동 시켜주는 a 태그

(3). 매개변수로 이벤트를 전달 받아 사용자가 입력한 url을 (4)의 함수로 전달

(4). 사용자가 입력한 url로 a 태그를 구현함

 

input 태그에 onChange 이벤트 핸들러를 등록했으므로 한 자 한 자 입력할 때마다 (3)의 함수가 호출됨

이 컴포넌트를 웹 페이지에 출력하고 url을 입력해보자


이번 글에서 배우는 내용은 리엑트 라기 보다는 JS의 함수에 대한 내용으로 만약 이해되지 않는 부분이 있거나 "오~ 이렇게 되네?" 라는 생각이 들었다면 JS의 함수를 꼭 공부하고 와야함

728x90
LIST

<< 학습 목표 >>

1. 리엑트에서 이미지 파일을 사용할 수 있다.

2. 리엑트에서 CSS 파일을 불러올 수 있다.

3. 컴포넌트를 역할별로 분리할 수 있다.

4. JS의 map 함수를 사용할 수 있다.

5. JS의 화살표(arrow) 함수를 사용할 수 있다.


지금까지 배운 컴포넌트를 활용해서 간단하게 주소록을 출력하는 컴포넌트를 만들어보자

 

먼저 주소를 출력할 컴포넌트를 만들자

프로젝트 -> src -> chapter02 -> Address.jsx 파일을 추가하고 아래 코드를 추가하자

import React from 'react'

function Address() {
    return(
        <div>
            <h1>특정한 주소의 정보를 출력할 컴포넌트입니다.</h1>
        </div>
    );
}

export default Address;

 

이 컴포넌트는 특정한 주소의 정보를 출력할 임시 컴포넌트임

이 컴포넌트를 화면에 출력하자

 

지금까지 잘 복습했다면 컴포넌트를 화면에 출력하려면 App.js 를 거치지 않아도 된다는 걸 기억할 것

바로 프로젝트 -> src -> index.js에 이 컴포넌트를 추가하고 웹 페이지에 출력해보자

 

앞으로 특정 컴포넌트를 웹 페이지에 출력하기 위한 방법인 index.js 를 보여주지 않을 것

지면을 많이 차지하고 지금까지 여러 차례 같이 해봤으므로...

 

앞으로 "특정 컴포넌트를 웹 페이지에 출력하자" 라고 하면 이와 같이 index.js에 적절히 추가해 보여주자


Address 컴포넌트를 제대로 구성해보자

 

Address 컴포넌트에서 이미지를 사용하기 위해 이미지 파일을 추가하자

프로젝트 -> src -> images 폴더를 추가한 후 아래 이미지를 다운 받아 images 폴더에 넣자

unknown_person2.png
0.01MB

<< images 폴더를 추가하고 해당 폴더에 이미지를 넣는 방법 >>

더보기

images 폴더를 추가하는 방법은 chapter02 폴더를 추가한 방법과 동일함

프로젝트 -> src 우클릭(1) -> 새 폴더(2) -> 폴더 이름을 images로 입력 후 엔터(3)

 

다운로드 받은 파일을 images 폴더로 넣을 때는 파일 탐색기에서 다운로드 폴더로 이동 후 옮길 파일을 드래그 해 images 폴더에 놓으면 됨


 

이번에는 Address 컴포넌트에서 사용할 CSS 파일을 추가하자

프로젝트 -> src -> css 폴더를 추가하고 css 폴더에 address.css 파일을 추가 한 후 아래 코드를 넣자

더보기

@charset "UTF-8";

.address_info {
    display: flex;
    border: 1px dashed;
    margin: 10px;
    padding: 10px;
}

.image_wrapper {
    margin-right: 20px;
}

.image_wrapper > img {
    width: 100px;
}

 

이번에는  Address 컴포넌트를 다음과 같이 수정하자

더보기

import React from 'react';
import person from '../images/unknown_person2.png';
import "../css/address.css";

function Address(props) {
    return(
        <div className="address_info">
            <div className="image_wrapper">
                <img src={person} alt="알 수 없는 사용자" />
            </div>
            <div className="text_wrapper">
                <p>이름 : {props.name}</p>
                <p>연락처 : {props.tel}</p>
                <p>주소 : {props.address}</p>
            </div>
        </div>
    );
}

export default Address;

 

<< Address 컴포넌트 설명 >>

더보기

번호 순서가 차례대로가 아닌 1, 3, 4, 2 인 점을 주의하자

1. 리엑트에서는 이미지 파일을 컴포넌트처럼 불러와 사용함

2. 리엑트에서는 불러온 이미지를 사용할 때 JSX 를 사용함

3. 리엑트에서는 css 파일을 사용할 때 import로 불러옴

4. 리엑트에서 태그의 class 속성을 지정할 때는 HTML과 다르게 className 으로 써야함

  class 속성을 지정할 때 속성명이 class가 아닌 className인 이유는 JS에서 class 키워드를 이미 사용하고 있기 때문에 class 속성명을 HTML에서 사용하던 그대로 class 라고 하면 JS의 class 키워드와 충돌이 생김

 

Address 컴포넌트는 props 로 전달 받은 이름, 연락처, 주소를 출력하고 있음



이번에는 AddressList 컴포넌트 ( 주소록 ) 를 만들자

프로젝트 -> src -> chapter02 -> AddressList.jsx 를 추가하고 아래 코드를 추가하자

import React from 'react';
import Address from './Address';

function AddressList() {
    let addressList = [
        {"name": "홍길동", "tel": "010-1111-1111", "address": "서울특별시"},
        {"name": "김철수", "tel": "010-2222-2222", "address": "인천광역시"},
        {"name": "고영희", "tel": "010-3333-3333", "address": "경기도"}
    ];

    return(
        <div>
            <Address name={addressList[0].name} tel={addressList[0].tel} address={addressList[0].address} />
            <Address name={addressList[1].name} tel={addressList[1].tel} address={addressList[1].address} />
            <Address name={addressList[2].name} tel={addressList[2].tel} address={addressList[2].address} />
        </div>
    );
}

export default AddressList;

 

<< 코드 설명 >>

(1). Address 컴포넌트를 사용하기 위해 불러옴

(2). 주소록을 구성할 주소 정보들 / JSON(주소 정보)을 배열에 담았음

(3). Address 컴포넌트의 props에 주소 정보를 보내 주소록을 구성

 

이제 AddressList 컴포넌트를 웹 페이지에 출력하자

혹시 "특정 컴포넌트를 웹 페이지에 출력하자" 라는 말을 잊었다면 다시 이 글의 제일 처음을 읽어보자


이번에는 마지막으로 JS의 map 함수를 활용해서 Address 컴포넌트를 더 쉽게 사용해보자

 

먼저 가지고 있는 JS 서적 또는 인터넷 또는 chatGPT 등에서 JS의 map 함수와 화살표 함수에 대해서 공부하고 오자

인터넷에서 검색할 때는 [ js map 함수 ] 등의 키워드로 검색하면 금방 좋은 글들이 나옴

 

<< map 함수를 사용해 컴포넌트 호출을 개선한 AddressList 컴포넌트 >>

import React from 'react';
import Address from './Address';

function AddressList() {
    let addressList = [
        {"name": "홍길동", "tel": "010-1111-1111", "address": "서울특별시"},
        {"name": "김철수", "tel": "010-2222-2222", "address": "인천광역시"},
        {"name": "고영희", "tel": "010-3333-3333", "address": "경기도"}
    ];

    return(
        <div>
            {
                addressList.map((address) => {
                    return (
                        <Address name={address.name} tel={address.tel} address={address.address} />
                    );
                })
            }
        </div>
    );
}

export default AddressList;
728x90
LIST