상세 컨텐츠

본문 제목

[React.js 1팀] 5장 리액트의 기본 기능 다루기 (1)

24-25/React.js 1

by mingging17 2024. 11. 8. 13:36

본문

728x90

1. 컴포넌트

개발자들은 리액트를 컴포넌트 기반의 UI 라이브러리라고 소개한다. 페이지의 모든 요소를 컴포넌트 단위로 쪼개어 개발하고, 완성된 컴포넌트를 마치 레고 조립하듯 하나로 합쳐 페이지를 구성하기 때문이다. 리액트의 핵심 개념 중 하나인 컴포넌트를 자세히 알아보겠다.

 

 

1-(1) 실습 환경 설정하기

앞서 리액트 앱을 생성했던 방법과 동일하다.

npx create-react-app .

 

Documents 폴더 아래 chapter5 폴더를 만든 다음, 해당 폴더 안에서 터미널을 열어 위 명령어로 새로운 리액트 앱을 만든다. 그 후 사용하지 않을 파일과 코드를 삭제하면 실습 환경 설정이 완료된다.



1-(2) 첫 컴포넌트 만들기

리액트 컴포넌트는 주로 자바스크립트의 클래스나 함수를 이용해 만든다. 클래스로 컴포넌트를 만드는 방식은 기본 설정 코드를 작성하는 등 함수로 만드는  컴포넌트에 비해 단점이 많아 지금은 선호하지 않는다.

 

함수 컴포넌트 만들기

함수를 이용해 App.js에서 첫 번째 리액트 컴포넌트를 만든다. App.js를 다음과 같이 수정한다.

import "./app.css";

function Header(){
	return(
    	<header>
        	<h1>header</h1>
        </header>
    );
}

function App() {
	return <div className="App"></div>;
}
export default App;

 

페이지에서 헤더 역할을 담당할 Header 컴포넌트를 만들었다. 이렇듯 함수를 이용하면 간단하게 리액트 컴포넌트를 만들 수 있다. 즉, 함수를 선언하고 해당 함수가 HTML요소를 반환하도록 만들면 된다. 함수를 사용해 만든 컴포넌트함수 컴포넌트라고 한다. 함수 선언식이 아니라 화살표 함수로도 컴포넌트를 만들 수 있다.

 

import "./app.css";

function Header = () => {
	return(
    	<header>
        	<h1>header</h1>
        </header>
    );
}

function App() {
	return <div className="App"></div>;
}
export default App;

 

함수 컴포넌트를 만들 때 한 가지 주의할 점은 컴포넌트 함수 이름의 첫 글자는 항상 영어 대문자여야 한다. 그 이유는 리액트 컴포넌트를 HTML 태그와 구분하기 위해서다. 컴포넌트 이름의 첫 글자를 대문자로 작성하지 않아도 에러가 발생하지는 않지만 정상적인 리액트 컴포넌트로 인식하지 않기 때문에 의도치 않은 결과가 나타날 수 있으며, 리액트가 제공하는 여러 유용한 기능도 사용할 수 없다.

 

컴포넌트를 페이지에 렌더링하기

Header 컴포넌트를 페이지에 렌더링하려면 App에서 이 컴포넌트를 자식 요소로 배치해야 한다. App 컴포넌트를 다음과 같이 수정한다.

import "./App.css";

const Header = () => {
	(...)
};

function App() {
	return (
    	<div className="App">
        	<Header />
        </div>
    );
}
export default App;

 

리액트는 다른 컴포넌트를 태그로 감싸 사용한다. 이때 App처럼 다른 컴포넌트를 return문 내부에 포함하는 컴포넌트를 '부모 컴포넌트', Header처럼 App의 return문에 포함된 컴포넌트를 '자식 컴포넌트'라고 한다.

 


1-(3) 컴포넌트의 계층 구조

리액트는 자식으로 배치한 컴포넌트를 부모와 함께 렌더링한다. 만약 페이지에 렌더링할 컴포넌트가 아래 그림과 같이 3개가 필요하다면, 각각을 컴포넌트로 만든 다음 App의 자식으로 배치해야한다. 리액트에서 컴포넌트를 페이지에 렌더링하려면, App의 자식으로 배치하거나 Header처럼 자식으로 이미 배치된 컴포넌트의 또 다른 자식으로 배치해야 한다.

 

리액트 컴포넌트는 위 그림처럼 부모-자식 관계라는 계층 구조를 형성한다. 컴포넌트의 계층 구조를 다른 말로 '컴포넌트 트리'라고 하며, 컴포넌트 트리에서 App은 항상 최상위에 존재하므로 이를 '루트 컴포넌트'라고 부른다.


1-(4) 컴포넌트별로 파일 분리하기

 하나의 파일에 여러 컴포넌트를 만들면 코드의 가독성이 떨어지기 때문에 보통 하나의 파일에 하나의 컴포넌트를 만든다. 파일을 분리한 후에는 import (컴포넌트 명) from "주소"를 통해 컴포넌트를 불러온다.

 

 

 

 


2. JSX

 

2-(1) JSX란?

리액트에서 컴포넌트는 자바스크립트 함수로 만드는데, 특이하게도 이 함수는 HTML값을 반환한다. 이렇듯 자바스크립와 HTML 태그를 섞어 사용하는 문법을 JSX라고 한다. JSX는 자바스크립트의 확장 문법이다. JSX는 공식 자바스크립트 문법은 아니지만 대다수의 리액트 개발자가 사용하는 문법이며, 리액트 공식 문서의 예제로도 사용한다. JSX 문법을 이용하면 HTML 태그에서 자바스크립트의 표현식을 직접 사용할 수 있다.

 

 

2-(2) JSX와 자바스크립트 표현식

표현식이란 값으로 평가되는 식이다. JSX는 자바스크립트 표현식을 HTML 태그와 함께 사용할 수 있어 가독성 있는 코드를 작성할 수 있다.

산술 표현식

산술표현식이란 숫자로 표현되는 식을 말한다. component 폴더의 Body.js를 다음과 같이 수정하면 1+2를 계산한 값인 3을 렌더링한 것을 확인할 수 있다.

function Body(){
	const numA=1;
 	const numB=2;
    	return (
    	    <div>
                <h1>body</h1>
            	<h2>{numA + numB}</h2>
            </div>
        );
}
export default Body;

 

문자열 표현식

문자열 표현식이란 문자열 또는 문자열로 평가되는 식을 말한다. component 폴더의 Body.js를 다음과 같이 수정한 결과 '안녕리액트'를 렌더링하는 것을 확인할 수 있다.

function Body(){
	const strA="안녕";
 	const strB="리액트";
    	return (
    	    <div>
                <h1>body</h1>
            	<h2>{strA + strB}</h2>
            </div>
        );
}
export default Body;

 


논리 표현식

논리 표현식이란 참이나 거짓으로 평가되는 식이다. component 폴더의 Body.js를 다음과 같이 수정하면 아무런 결과를 렌더링하지 않았지만 이는 오류가 아니다.

function Body() {
    const boolA = true;
    const boolB = false;
    return (
    	<div>
            <h1>body</h1>
            <h2>{boolA || boolB}</h2>
        </div>
    );
}
export default Body;

 

논리 표현식의 결과인 불리언 값은 숫자나 문자열과 달리 페이지에 렌더링되지 않는다. 만일 불리언 값을 페이지에 렌더링하고 싶다면, 형 변환 함수를 이용해 문자열로 바꿔 주어야 한다.

 


사용할수 없는 값

JSX는 값을 반환하는 자바스크립트 표현식을 사용할 수 있다. 그러나 모든 값을 사용할 수 있는 것은 아니다. 원시 자료형에 해당하는 숫자, 문자열, 불리언, null, undefined를 제외한 값을 사용하면 오류가 발생한다.

 


2-(3) JSX문법에서 지켜야 할 것들

닫힘 규칙

JSX의 모든 태그는 여는 태그가 있으면 반드시 닫는 태그도 있어야 한다는 규칙이다. HTML태그 중 <img>,<input>은 닫힘 태그 없이도 사용할 수 있는데, JSX에서 이 태그를 사용하려면 <img />,<input />과 같이 닫힘 태그를 반드시 병기해야 한다.

최상위 태그 규칙

JSX가 반환하는 모든 태그는 반드시 최상위 태그로 감싸야 한다.

function Body() {
	return (
        <div>div 1</div>
        <div>div 2</div>
    );
}
export default Body;

 

위 코드를 실행하면 Body 컴포넌트의 return문 안에 최상위 태그가 존재하지 않아 오류가 발생한다. HTML태그를 최상위 태그로 사용하지 않으려면, 다음과 같이 <React.Fragment>태그를 사용하면 된다.

import React from "react";

function Body() {
    return(
        <React.Fragment>
            <div>div 1</div>
            <div>div 2</div>
        </React.Fragment>
    );
}
export default Body;


<React.Fragment>로 다른 태그를 감싸면 최상위 태그를 대체하는 효과가 있다. 단 페이지에서 <React.Fragemnt> 태그는 렌더링되지 않는다. <React.Fragment> 대신 빈 태그 '<></>'를 사용할 수도 있다.

 

 

2-(4)조건문 렌더링

리액트 컴포넌트가 조건식 결과에 따라 각기 다른 값을 페이지에 렌더링하는 것을 조건문 렌더링이라고 한다.

삼항 연산자를 활용한 조건부 렌더링

삼항 연산자를 이용해 변수 num의 값이 2로 나누어 떨어지면 짝수, 그렇지 않으면 홀수를 반환한다.

import React from "react";

function Body() {
    const num = 19;
    return (
      <>
        <h2>
          {num}은(는) {num % 2 === 0 ? "짝수" : "홀수"} 입니다.
        </h2>
      </>
    );
}
export default Body;

 

if 조건문은 표현식에 해당하지 않기 때문에 JSX와 함께 사용할 수 없지만, 표현식인 삼항 연산자를 이용하면 조건에 따라 다른 값을 렌더링할 수 있다.

 

조건문을 이용한 조건부 렌더링

조건문은 자바스크립트의 표현식이 아니기 때문에 JSX와 함께 사용할 수 없지만, 다음과 같이 조건에 따라 컴포넌트가 반환하는 값을 다르게 표히하도록 만들 수 있다.

import React from "react";

function Body() {
  const num = 200;
  
  if (num % 2 === 0) {
    return <div>{num}은(는) 짝수입니다</div>;
  } else {
    return <div>{num}은(는) 홀수입니다</div>;
  }
}
export default Body;

 

 

삼항 연산자는 코드가 매우 간결하지만, 자주 사용할 경우 가독성을 해칠 우려가 있다. 그리고 삼항 연산자는 다중 조건을 작성하기 힘들다. 반면 조건문은 가독성은 좋으나 기본적으로 작성해야 할 코드가 많고 중복 코드가 발생할 우려가 있다.

 

2-(5) JSX 스타일링

스타일링이란 CSS와 같은 스타일 규칙을 이용해 요소의 크기, 색상 등을 결정하는 일이다.


인라인 스타일링

인라인 스탕일링이란 JSX 문법 중 하나로 HTML의 style 속성을 이용해 직접 스탕일을 정의하는 방법이다.

function Body () {
    return (
        <div style={{backgroundColor: "red", color: "blue" }}>
            <h1>body</h1>
        </div>
    );
}
export default Body;

 

JSX의 인라인 스타일링은 style={{스타일 규칙들}}과 같은 문법으로 작성한다. 문자열로 작성하는 HTML의 인라인 스타일링과는 달리, JSX의 인라인 스타일링은 객체를 생성한 다음 각각의 스타일을 프로퍼티 형식으로 작성한다.

 

인라인 스타일링은 하나의 파일 안에서 UI 표현을 위한 HTML과 스타일을 위한 CSS 규칙을 함께 작성할 수 있다는 장점이 있다. 그러나 페이지가 스타일을 계싼할 때 불필요한 연산을 수행할 가능성이 있고, 스타일 규칙이 많으면 코드가 복잡해져 가독성이 떨어진다.

스타일 파일 분리

html에서는 스타일을 정의한 css파일 따로 작성 후 <link rel='style sheet' href='css 파일경로'>형식으로 불러와 사용한다.  JSX도 마찬가지로 css파일을 만들고 불러와 적용할 수 있다.

css 파일

.body {
    background-color: green;
    color: blue;
}



body.js 파일

import "./Body.css"

function Body () {
    return (
        <div className="body">
            <h1>body</h1>
        </div>
    );
}
export default Body;

 

 


3. 컴포넌트에 값 전달하기

리액트 앱을 만들다 보면 컴포넌트가 다른 컴포넌트에 값을 전달해야 하는 상황이 생긴다. 컴포넌타 간에 값을 주고받는 방법을 알아보겠다.

 


3-(1) Props란?

리액트에서는 부모가 자식 컴포넌트에 단일 객체 형태로 값을 전달할 수 있다. 이 객체를 리액트에서는 Props라고 한다.
Props는 Properties의 줄임말로 속성이라는 뜻이다.

리액트에서는 보통 재사용하려는 요소를 컴포넌트로 만든다. 예를 들어 게시판 페이지를 리액트로 만든다고 가정해 보자. 사용자가 게시판에서 작성한 글은 게시물 리스트에서 하나의 항모긍로 표시된다. 그런데 이 리스트에 존재하는 여러 게시물의 항목은 내용은 각각 다르지만, 모두 동일한 구조다. 리액트에서는 내용은 다른지만 구조가 같은 요소를 주로 컴포넌트로 만든다. 여러 게시물 리스트를 페이지에 표시할 때는 이 컴포넌트를 반복해 렌더링하고, 게시물 각각의 내용은 Props로 전달한다.

 

보통 리액트에서 컴포넌트 값을 전달하는 경우는 세부 사항들, 즉 컴포넌트의 속성을 지정하는 경우가 대부분이다. 따라서 컴포넌트에 값을 전달하는 속성들이라는 점에서 Properties라고 부르며, 이를 간단히 줄여 Props라고 한다.

 


3-(2) Props로 값 전달하기

Props는 부모만이 자식 컴포넌트에 전달할 수 있다. 그 역은 성립하지 않는다.

Props로 하나의 값 전달하기

import './App.css';
import Header from "./component/Header";
import Body from "./component/Body";
import Footer from "./component/Footer";

function App() {
  const name = "김덕성";
  return (
  <div className="App">
    <Header />
    <Body name={name}/>
    <Footer />
  </div>
  );
}

export default App;

 

App.js파일을 위 처럼 수정하자. Props를 전달하려는 자식 컴포넌트 태그에서 이름={값} 형식으로 작성하면 된다. App에서 전달한 Props를 Body 컴포넌트에서 사용할 때는 밑 처럼 Body.js파일을 수정하면 된다.

function Body (props) {
    console.log(props);
    return <div className="body">{props.name}</div>;
}
export default Body;



Props로 여러 개의 값 전달하기

App 컴포넌트를 다음과 같이 수정한다.

function App() {
  const name = "김덕성";
  return (
  <div className="App">
    <Header />
    <Body name={name} location={"부천시"}/>
    <Footer />
  </div>
  );
}


Body 컴포넌트에서 Props로 전달된 2개의 값을 사용한다.

function Body(props) {
  console.log(props);
  return (
    <div className="body">
      {props.name}은 {props.location}에 거주합니다
    </div>
  );
}



구조 분해 할당으로 여러 개의 값 사용하기

Props로 전달된 값이 많으면, 이 값을 사용할 때마다 객체의 점 표기법을 사용해야 해서 불편하다. 그런데 Props는 객체이므로 구조 분해 할당하면 간편하게 사용할 수 있다. 다음과 같이 Body컴포넌트를 수정하자.

function Body(props) {
  const {name,location} = props;
  console.log(name,location);
  return (
    <div className="body">
      {name}은 {location}에 거주합니다
    </div>
  );
}
export default Body;

 

저장 후 결과를 콘솔의 결과를 보면 결괏값은 구조 분해 할당했기에 객체가 아닌 상숫값임을 알 수 있다. Body 컴포넌트의 매개변수에서 구조 분해 할당하면 더 간결한 코드를 작성할 수 있다.

function Body({name,location}) {
console.log(name,location);
  return (
    <div className="body">
      {name}은 {location}에 거주합니다
    </div>
  );
}
export default Body;

 

 

스프레드 연산자로 여러 개의 값 쉽게 전달하기

반대로 부모 컴포넌트에서 Props로 전달할 값이 많으면, 값을 일일이 명시해야 하므로 불편할 뿐만 아니라 가독성도 떨어진다. 이때 Props로 값을 하나의 객체로 만든 다음, 스프레드 연산자를 활용해 전달하면 훨씬 간결하게 코드를 작성할 수 있다.

(...)

function App() {
  const BodyProps = {
    name: "이정환",
    location: "부천시",
  };
  
  return {
    <div className="App">
      <Header />
      <Body {...BodyProps} />
      <Footer />
    </div>
  );
}
export default App;

 

기본값 설정하기

defaultProps를 이용하면 컴포넌트가 받을 Props의 기본값을 미리 설정할 수 있기 때문에 오류를 미연에 방지할 수 있다.



3-(3) Props로 컴포넌트 전달하기

Props로는 자바스크립트 값뿐만 아니라 컴포넌트도 전달할 수 있다. App에서 Body로 컴포넌트를 하나 전달하겠다.

(...)

function ChildComp() {
  return <div>child componenet</div>;
}

function App() {
  return (
    <div className="App">
      <Header />
      <Body>
        <ChildComp />
      </Body>
      <Footer />
    </div>
  );
}
export default App;

 

Body 컴포넌트의 자식 요소로 ChildComp를 배치했다. 리액트에서는 자식 컴포넌트에 또 다른 컴포넌트를 배치하면, 배치된 컴포넌트는 자동으로 Props의 children 프로퍼티에 저장되어 전달된다. children 프로퍼티에 저장된 자식 컴포넌트를 사용하겠다.

function Body({children}) {
  console.log(children);
  return <div className="vody">{children}</div>;
}
export default Body;

 

Props의 children 프로퍼티로 전달되는 자식 컴포넌트는 값으로 취급하므로 JSX의 자바스크립트 표현식으로 사용할 수 있다. children에는 컴포넌트 ChildComp가 저장되어 있기 때문에 해당 컴포넌트를 렌더링한다.저장하고 결과를 콘솔과 페이지에서 확인하자. 컴포넌트를 개발자 도구의 콘솔에서 출력하면 객체 형식의 값을 출력한다. 앞서 JSX에서는 자바스크립트 표현식이 객체를 평가할 경우 오류가 발생한다고 했지만, 이 객체의 경우 리액트 컴포넌트를 표현한 것이므로 오류가 발생하지 않는다.

 

 

 


출처: 이정환, 『한 입 크기로 잘라먹는 리액트』, 프로그래밍인사이트(2023), p44-87.

Corner React.js1
Editor: Ninanyu

728x90

관련글 더보기