useForm

useForm은 react-hook-form라이브러리에서 제공하는 핵심 훅으로, 폼 데이터를 효율적으로 관리하고 유효성 검사를 쉽게 구현할 수 있게 해준다. useform을 사용하면 폼의 상태와 유효성 검사로직을 간결하게 작성할 수 있어 코드의 유지 보수성이 높아진다. 

 

useForm의 기본구조 

useForm을 호출하게 되면 여러기능을 제공하는 객체를 반환한다. 이 객체에는 폼의 상태와 유효성 검사를 위한 다야한 도구들이 포함되어 있다. 다음은 기본적으로 사용되는 형식이다.

import { useForm } from "react-hook-form";

const { register, handleSubmit, formState, reset } = useForm();

 

useForm의 주요 옵션 

1. useForm 함수의 옵션

useForm은 여러 설정 옵션을 받습니다. 다음은 주요 옵션들입니다.

  • defaultValues
    • 폼의 초기 값을 설정합니다.
    • 객체 형태로 { name: "", email: "", password: "" } 등과 같이 각 필드의 초기값을 지정합니다.
    • 예시:
const { register, handleSubmit } = useForm({
  defaultValues: { name: "", email: "", password: "" },
});
  • mode
    • 유효성 검사를 언제 실행할지를 지정합니다.
    • 주요 값:
      • "onSubmit": 폼 제출 시 유효성 검사 실행 (기본값).
      • "onBlur": 입력 필드가 포커스를 잃을 때 검사.
      • "onChange": 입력 값이 변경될 때마다 검사.
      • "onTouched": 입력이 처음 수정된 이후부터 검사.
    • 예시
const { register, handleSubmit } = useForm({ mode: "onBlur" });
  • resolver
    • 외부 유효성 검사 라이브러리(예: Yup, Zod)와 연동할 때 사용합니다.
    • 커스텀 유효성 검사 로직을 추가할 수 있습니다.
    • 예시
import { yupResolver } from '@hookform/resolvers/yup';
import * as yup from 'yup';

const schema = yup.object({
  name: yup.string().required(),
  email: yup.string().email().required(),
});

const { register, handleSubmit } = useForm({
  resolver: yupResolver(schema)
});

 

useForm 반환값 

useForm은 객체 형태로 여러 가지 유용한 메서드와 속성을 반환합니다.

  • register
    • 각 입력 필드에 이 메서드를 연결하여 react-hook-form이 필드의 상태와 유효성 검사를 관리하게 합니다.
    • 필드의 이름과 옵션을 매개변수로 받습니다.
    • 예시
       
<input {...register("name")} />
<input {...register("email", { required: "Email is required" })} />
  • handleSubmit
    • 폼 제출 시 실행할 함수로, 유효성 검사를 완료하고 성공 시 콜백 함수를 호출합니다.
    • onSubmit 콜백 함수로 폼 데이터를 전달하며, 에러 발생 시 콜백은 실행되지 않습니다.
    • 예시
const onSubmit = (data) => {
  console.log(data);
};

<form onSubmit={handleSubmit(onSubmit)}>
  <input {...register("name")} />
  <button type="submit">Submit</button>
</form>
  • formState
    • 폼의 현재 상태를 나타내는 객체로, 다양한 상태값을 포함합니다.
      • isDirty: 폼이 수정된 상태인지 확인.
      • isValid: 폼의 유효성 여부.
      • errors: 각 필드의 에러 메시지 및 상태.
    • 예시
const {
  formState: { errors, isDirty, isValid },
} = useForm();

<div>{errors.email && <span>{errors.email.message}</span>}</div>
  • reset
    • 폼의 모든 필드를 초기 상태로 되돌리거나 특정 값을 설정해 리셋할 수 있습니다.
    • 예시
<button type="button" onClick={() => reset()}>Reset</button>
  • watch
    • 폼 필드의 값을 실시간으로 관찰하여 값의 변화를 감지합니다.
    • 특정 필드의 값이나 전체 폼 값을 반환할 수 있습니다.
    • 예시
const nameValue = watch("name");
  • setValue
    • 특정 필드의 값을 수동으로 설정할 때 사용합니다.
    • 예시
setValue("name", "John Doe");
  • getValues
    • 폼의 전체 값이나 특정 필드의 값을 즉시 가져옵니다.
    • 예시
const allValues = getValues();

서버 컴포넌트와 클라이언트 컴포넌트란? 

서버 컴포넌트 : 기본적으로 Next.js는 모든 컴포넌트를 서버에서 렌더링한다. 이 방식은 더 빠른 페이지 로딩 속도와 SEO 최적화에 유리하다. 서버 컴포넌트는 서버에서 실행되기 때문에 브라우저에 접근할 수 없는 데이터나 API에 접근할 수 있다. 

클라이언트 컴포넌트: 서버에서 렌더링하는 대신 브라우저에서 실행되는 컴포넌트로, React의 상태와 훅을 사용할 수 있다. 브라우저 환경에서의 동적인 상호작용을 필요로 할때 사용된다. 

 

use client 의 역할

next.js에서는 기본적으로 모든 컴포넌트를 서버에서 렌더링하낟. 하지만 특정 컴포넌트에서 브라우저 전용기능(상태관리, 이벤트 리스너 등)을 사용해야 한다면, 그 컴포넌트를 클라이언트 컴포넌트로 명시적으로 선언해야한다.

 

이때, 파일의 맨 위에 "use client";지시어를 추가하면, Next.js는 해당 컴포넌트를 클라이언트 측에서 렌더링한다.

 

사용예제

// app/counter/page.tsx
"use client";

import React, { useState } from 'react';

const Counter = () => {
  const [count, setCount] = useState(0);

  return (
    <div>
      <p>Current count: {count}</p>
      <button onClick={() => setCount(count + 1)}>Increment</button>
    </div>
  );
};

export default Counter;

위 코드에서는 useState와 onClick이벤트가 사용되므로, 반드시 클라이언트에서 렌더링되어야 한다.

 

use client사용시 주의 사항 

- 클라이언트 컴포넌트는 서버 컴포넌트보다 렌더링 성능이 낮을 수 있으므로, 필요한 경우에만 사용해야한다. 

- 서버 컴포넌트 안에서 클라이언트 컴포넌트를 포함할 수 있지만, 반대는 불가능하다. 즉 클라이언트 컴포넌트 내에서 서버 컴포넌트를 포함할 수 없다. 

 

서버컴포넌트와 클라이언트 컴포넌트 혼합

클라이언트 컴포넌트를 서벗 컴포넌트 내에 포함하면, 서버에서 필요한 데이터 처리는 서버 컴포넌트가 담당하고 클라이언트에서의 상호작용은 클라이언트 컴포넌트가 담당하는 식으로 역할을 분리 할 수 있다.

// app/dashboard/page.tsx (서버 컴포넌트)
import React from 'react';
import Counter from './Counter'; // 클라이언트 컴포넌트 import

const Dashboard = async () => {
  const data = await fetchData(); // 서버에서 데이터 가져오기

  return (
    <div>
      <h1>Dashboard</h1>
      <p>Data from server: {data}</p>
      <Counter /> {/* 클라이언트 컴포넌트 포함 */}
    </div>
  );
};

export default Dashboard;

위의 예제엇 Dashboard는 서버 컴포넌트이고 Counter는 클라이언트 컴포넌트이다. 서버 컴포넌트에서는 서버에서만 실행할 수 잇는 데이터를 불러오고, 클라이언트 컴포넌트에서는 사용자 상호작용이 필요한 부분을 담당하게 된다. 

 

 

Messenger앱에서 처음에는 로그인 페이지를 만드는 것을 진행했다. 

 

프로젝트 시작 

일단 처음에는 아래의 명령어를 사용해서 프로젝트를 시작했다. 리액트의 create-react-app과 다를 건 없다. 

npm create-next-app --typescript

typescript를 사용할 것이기에 --typescript를 붙여서 명령어를 작성했다. 

 

생성된 프로젝트의 파일구조를 확인해보면 app파일에는 page.tsx와 layout.tsx파일이 존재한다.

page.tsx

page.tsx는 각 페이지의 메인 콘텐츠를 정의하는 파일이다. next.js에서 page.tsx파일은 URL경로와 직접 연결이 되는데 예를 들어, app/about/page.tsx파일이 있다면 /about경로에 대응하는 페이지가 생성된다. 이 파일에는 해당 페이지에서만 필요한 컴포넌트와 콘텐츠가 들어가고, React컴포넌트로 작성된다. 또한 이곳에 API데이터를 가져오거나 페이지 렌더링과 관련된 로직을 추가할 수 도있다.

 

layout.tsx

layout.tsx는 페이지의 공통 레이아웃을 정의하는 파일이다. 여기에는 모든 페이지에서 공통으로 사용되는 UI요소를 포함할 수 있다. layout.tsx파일은 특정 폴더에 위치한 모든 페이지에 적용되는 레이아웃 역할을 하며, page.tsx에서 정의된 개별 컨텐츠가 layout.tsx의 children을 통해서 삽입된다. 예를 들어 app/about/layout.tsx파일이 있다면 about폴더의 모든 페이지에 이 레이아웃이 적용된다.

 

이 프로젝트에서는 app/(site)/page.tsx 형태로 진행이 되었다. 여기서 page.tsx는 index의 역할을 하게된다. 

 

AuthForm.tsx

이 파일은 app/(site)/components/AuthForm.tsx에 위치한다. 

 

클라이언트 컴포넌트

이 페이지의 상단에 "use client"라는 녀석이 있는데 클라이언트 컴포넌트라는 친구이다. 

이 친구는 서버에서 렌더링하는 대신브라우저에서 실행되는 컴포넌트로 , React의 상태와 훅을 사용할 수 있다. 브라우저의 동적인 상호작용을 필요로 할 때 사용된다. 

 

리터럴 타입

type Variant = "LOGIN" | "REGISTER";

이건 typescript에서 타입을 별도로 지정하는 문법이다. 여기서는 variant타입이 지정되면 LOGIN값 또는 REGISTER 중 하나의 값만 가질 수 있게 된다. 이러한 경우에는 코드의 가독성이 좋아지고 잘못된 값을 갖지 않도록 타입검사를 해주는 역할도 한다. 

 

상태관리

variant: 현재 컴포넌트가 로그인 상태인지 회원가입 상태인지를 관리합니다.

isLoading: 요청 중인 상태를 나타내며, 로딩이 완료될 때까지 일부 UI요소를 비활성화합니다.

 

toggleVariant()

useCallback을 사용하여 variant상태를 토글하는 함수이다. useCallback을 사용해서 variant상태를 토글한다.

- 로그인 중이면 회원가입 상태를 변경하고, 회원가입 상태라면 로그인 상태로 변경한다. 

- variant를 의존성으로 지정해서 variant가 변경될 때만 새로 생성되도록 한다. 

const toggleVariant = useCallback(() => {
  if (variant === "LOGIN") {
    setVariant("REGISTER");
  } else {
    setVariant("LOGIN");
  }
}, [variant]);

 

FieldValues

- FieldValues는 폼의 입력 데이터 형태를 정의하는 TypeScript 타입입니다.

- 각 폼 필드가 어떤 데이터를 포함할 수 있는지 유연하게 지정할 수 있도록 합니다. FieldValues를 기본 타입으로 사용하여 폼 입력 필드의 다양한 값을 저장할 수 있습니다

- 예시 { name: string, email: string, password: string } 등의 형태로 입력 데이터가 저장될 수 있습니다.

 

SubmitHandler

- 폼이 제출되었을 때 호출되는 함수의 타입을 정의하는 타입이다.

- 폼 제출 이벤트에 맞는 데이터 타입을 보장하기 위해 사용된다. 폼의 데이터 타입이 FieldValues일 때 submitHandler로 이 타입을  설정해주면, 폼 제출 시 해당 데이터가 onSubmit함수의 매개변수로 전달된다.

 

useForm

- useForm은 react-hook-form라이브러리 훅으로, 폼 데이터를 다루고 유효성 검사를 돕는다. 

- register는 입력필드를 useForm에 등록하며, handleSubmit는 폼제출을 처리한다. 

- formState.error는 입력값 유효성 검사 오류를 담고 있다. 

- defaultValues를 통해 폼 초기 값을 지정한다. 

const { register, handleSubmit, formState: { errors } } = useForm<FieldValues>({
  defaultValues: { name: "", email: "", password: "" },
});

 

onSubmit함수 

handlesubmit로  호출되는 폼 제출함수이다. 

- variant가 register면 회원가입 login이면 로그인 처리한다. 

const onSubmit: SubmitHandler<FieldValues> = (data) => {
  setIsLoading(true);
  if (variant === "REGISTER") {
    // 회원가입 로직 추가
  }
  if (variant === "LOGIN") {
    // 로그인 로직 추가
  }
};

SubmitHandler에 대해서 공부해보자.. 이게 뭘까

 

JSX렌더링 부분

여긴 뭐 전부다 설명할 필요는 없을 거 같다. 

중요하거나 새로운 부분들만 정리.

 

사용된 tailwind CSS

#전체 레이아웃 컨테이너
mt-8: 상단 여백 (margin-top) 적용.
sm:mx-auto: 작은 화면 이상에서 수평 중앙 정렬.
sm:w-full: 작은 화면 이상에서 너비 100%.
sm:max-w-md: 작은 화면 이상에서 최대 너비 md (768px)

#내부 카드 레이아웃
bg-white: 배경색을 흰색으로 설정.
px-4: 좌우 패딩 1rem.
py-8: 상하 패딩 2rem.
shadow: 기본 그림자 효과.
sm:rounded-lg: 작은 화면 이상에서 큰 모서리 반경.
sm:px-10: 작은 화면 이상에서 좌우 패딩을 2.5rem로 설정.

#폼 레이아웃
space-y-6: 각 자식 요소 사이에 수직 여백 1.5rem 적용

#입력 필드 상단 구분선
relative: 상대 위치 설정.
flex: Flexbox로 레이아웃 설정.
justify-center: 가운데 정렬.
text-sm: 텍스트 크기를 작게 설정.

#구분선
w-full: 너비 100%.
border-t: 상단 테두리만 적용.
border-gray-300: 회색 톤의 테두리 색상 적용.

#버튼 스타일
mt-6: 상단 여백 1.5rem 적용.
flex: Flexbox 레이아웃 설정.
gap-2: Flexbox 자식 요소 간 0.5rem 간격 설정.

#로그인/회원가입 전환링크
flex: Flexbox로 레이아웃 설정.
gap-2: 요소 간 간격을 0.5rem 적용.
justify-center: 자식 요소를 가운데 정렬.
text-sm: 텍스트 크기를 작게 설정.
mt-6: 상단 여백 1.5rem 적용.
px-2: 좌우 패딩 0.5rem 적용.
text-gray-500: 회색 톤의 텍스트 색상.

#전환링크 스타일
underline: 텍스트 밑줄 추가.
cursor-pointer: 마우스 커서를 포인터 모양으로 설정.

 

그 동안 자바스크립트나 리액트와 관련된 내용들을 문서로만 학습을 진행하다보니 실제로 애플리케이션이 구현되었을 때 어떻게 적용되어야하는지 잘 와닿는 느낌이 없었다. 

그리하여 유튜브에 올라와있는 다양한 클론 코딩 영상을 보면서 학습을 진행해보려고 한다. 블로그에 기록되는 내용은 거창하게 누군가에게 보여주기 위한 정보성 글보다는 나의 공부 흔적을 남기기 위해서 사용할 생각이며 클론 코딩을 하면서 학습하는 내용들을 그 때 그때 정리하여 기록하려고 한다. 

 

https://www.youtube.com/watch?v=PGPGcKBpAk8&t=720s

 

첫 클론코딩으로 선택한 프로젝트는 페이스북에서 사용되는 메신저 앱이다. 

채팅앱의 동작원리와 프론트엔드에서 어떻게 구현되는지 배우기 편할 것이라고 생각된다. 

이 프로젝트에서 사용되는 기술들은 Next.js, React, Tailwind, Prisma. MongoDB, NextAuth, Pusher 등이 있다. 

 

매일 매일 기록은 못하더라도 중요한 것은 모든 코드들을 이해하고 넘어가는 과정이라고 생각한다.

그냥 따라 치기만 하면서 진행된다면 아마 .. 이 프로젝트가 끝났을 때 나 이거 왜했지 하는 생각이 들 수도 있을 것이다. 

그러니 부디 매일 매일 꾸준히 잘 기록해서 공부하고 그러도록 하자. ..

 

화이팅!

 

 

 

 

 

 

 

 

React에서 컴포넌트를 만드는 방식은 함수형 컴포넌트클래스형 컴포넌트 두 가지로 나눌 수 있습니다. 최근 React에서는 함수형 컴포넌트와 Hooks를 주로 사용하지만, 클래스형 컴포넌트 역시 여전히 많은 프로젝트에서 사용됩니다. 이번 블로그 글에서는 함수형 컴포넌트와 클래스형 컴포넌트를 비교하며 각 방식의 차이점, 장단점, 그리고 사용 사례에 대해 살펴보겠습니다.

 

1. 함수형 컴포넌트란?

함수형 컴포넌트는 간단하게 함수로 정의되며, JSX를 반환하는 구조입니다. 상태를 관리하거나 생명주기 메서드를 사용하기 위해 React의 Hooks를 활용할 수 있습니다. 최신 React 애플리케이션에서는 주로 함수형 컴포넌트가 사용되고 있습니다.

import React, { useState } from 'react';

const Counter = () => {
  const [count, setCount] = useState(0);

  return (
    <div>
      <h1>Count: {count}</h1>
      <button onClick={() => setCount(count + 1)}>Increment</button>
    </div>
  );
};

export default Counter;

함수형 컴포넌트의 주요 특징

  • 간결함: 함수형 컴포넌트는 코드가 간단하고 선언적입니다.
  • Hooks 사용: useState, useEffect 등과 같은 Hooks를 사용해 상태 관리 및 생명주기와 유사한 기능을 처리할 수 있습니다.
  • 가벼움: 클래스형 컴포넌트보다 메모리 측면에서 더 효율적입니다.

2. 클래스형 컴포넌트란?

클래스형 컴포넌트는 class 키워드를 사용해 정의되며, state와 생명주기 메서드(componentDidMount, componentDidUpdate 등)를 통해 컴포넌트의 상태와 행동을 관리합니다. 함수형 컴포넌트보다 문법이 복잡하지만, React의 초기부터 사용된 방식입니다.

import React, { Component } from 'react';

class Counter extends Component {
  constructor(props) {
    super(props);
    this.state = {
      count: 0,
    };
  }

  increment = () => {
    this.setState({ count: this.state.count + 1 });
  };

  render() {
    return (
      <div>
        <h1>Count: {this.state.count}</h1>
        <button onClick={this.increment}>Increment</button>
      </div>
    );
  }
}

export default Counter;

클래스형 컴포넌트의 주요 특징

  • 클래스 사용: ES6 class 문법을 사용하여 정의됩니다.
  • 생명주기 메서드: 컴포넌트가 마운트되거나 업데이트될 때 실행되는 생명주기 메서드를 제공합니다.
  • this 사용: 클래스 컴포넌트에서는 this 키워드를 사용해 상태나 메서드에 접근해야 합니다.

3. 함수형 컴포넌트와 클래스형 컴포넌트 비교

항목함수형 컴포넌트클래스형 컴포넌트

정의 방식 함수로 정의 class로 정의
상태 관리 useState와 같은 Hooks를 사용 this.state와 setState() 사용
생명주기 관리 useEffect를 통해 생명주기 관리 가능 componentDidMount, componentDidUpdate 등 생명주기 메서드 사용
코드 구조 간결하고 선언적 상대적으로 복잡하고 명령적
this 사용 필요 없음 this로 상태 및 메서드에 접근
Hooks 지원 완벽히 지원 (useState, useEffect 등) Hooks 사용 불가

1) 코드의 간결성

함수형 컴포넌트는 함수로 정의되고 Hooks를 통해 상태나 생명주기 메서드를 쉽게 사용할 수 있어 코드가 더 간결합니다. 클래스형 컴포넌트는 constructor, this 키워드를 사용해야 해서 더 복잡한 구조를 가집니다.

2) 생명주기 관리

클래스형 컴포넌트는 생명주기 메서드를 사용해 컴포넌트의 마운트, 업데이트, 언마운트 등의 시점에 특정 동작을 정의할 수 있습니다. 함수형 컴포넌트에서는 useEffect 훅을 사용해 이와 같은 기능을 구현할 수 있습니다.

 

클래스형 컴포넌트의 생명주기 메서드 예시

class MyComponent extends React.Component {
  componentDidMount() {
    console.log('Component mounted');
  }

  componentDidUpdate(prevProps, prevState) {
    console.log('Component updated');
  }

  render() {
    return <div>My Component</div>;
  }
}

 

함수형 컴포넌트에서 useEffect를 이용한 생명주기 관리

import React, { useEffect } from 'react';

const MyComponent = () => {
  useEffect(() => {
    console.log('Component mounted or updated');
  });

  return <div>My Component</div>;
};

3) Hooks의 등장

Hooks의 도입으로 함수형 컴포넌트가 더욱 강력해졌습니다. 이전에는 클래스형 컴포넌트에서만 상태 관리 및 생명주기 메서드를 사용할 수 있었지만, Hooks 덕분에 함수형 컴포넌트에서도 이를 사용할 수 있게 되어 함수형 컴포넌트가 더 선호되고 있습니다.

 

4. 결론: 언제 어떤 컴포넌트를 사용해야 할까?

클래스형 컴포넌트는 기존 코드나 레거시 프로젝트에서 많이 사용되며, 아직도 유지보수가 필요할 때 유용할 수 있습니다. 하지만 새롭게 프로젝트를 시작한다면, 함수형 컴포넌트와 Hooks를 사용하는 것이 더 효율적입니다.

  • 레거시 코드: 기존에 클래스형 컴포넌트를 많이 사용해온 프로젝트라면 유지보수 차원에서 클래스를 계속 사용해야 할 수도 있습니다.
  • 새 프로젝트: 최신 기능을 활용하고 더 간결한 코드를 작성하고 싶다면 함수형 컴포넌트와 Hooks를 사용하는 것이 좋습니다.

React의 방향성은 함수형 컴포넌트에 무게를 두고 발전하고 있으므로, 앞으로는 함수형 컴포넌트가 기본 선택지가 될 것입니다.

리액트에서는 컴포넌트라는 개념이 매우 중요합니다. 컴포넌트에 대해서 확인해보고 이와 더불어 state와 props에 대한 내용도 확인해 봅시다.

React 컴포넌트란?

React에서 컴포넌트는 UI를 구성하는 가장 작은 단위로, 독립적이고 재사용 가능한 코드 조각입니다. 이 컴포넌트는 각자 자신의 상태를 관리할 수 있고, 서로 데이터를 주고받으며 동작합니다. 이 컴포넌트는 다음과 같은 두 가지 형태로 작성할 수 있습니다:

  • 함수형 컴포넌트 (Functional Components): 함수 형태로 정의된 컴포넌트.
  • 클래스형 컴포넌트 (Class Components): 클래스를 이용해 정의된 컴포넌트. (최근에는 함수형 컴포넌트와 Hooks를 더 많이 사용함)

함수형 컴포넌트의 예시

const MyComponent = () => {
  return (
    <div>
      <h1>Hello, React!</h1>
    </div>
  );
};

 

컴포넌트에서 STATE

State는 컴포넌트 내에서 변경 가능한 데이터를 관리하는 데 사용됩니다. 예를 들어, 사용자 입력, 버튼 클릭 등으로 인해 변할 수 있는 값을 다룰 때 useState 훅을 사용합니다. 쉽게 이야기하면 변수와 비슷한 개념이라고 생각하면 쉬울 거 같습니다.

import React, { useState } from 'react';

const Counter = () => {
  // useState로 count 상태 선언
  const [count, setCount] = useState(0);

  return (
    <div>
      <h1>Count: {count}</h1>
      {/* 버튼 클릭 시 count 상태 변경 */}
      <button onClick={() => setCount(count + 1)}>Increment</button>
    </div>
  );
};

export default Counter;

 

  • useState는 현재 상태(count)와 상태를 업데이트하는 함수(setCount)를 반환합니다.
  • 위 예시에서 버튼을 클릭할 때마다 count 값이 1씩 증가하고, 그 값이 화면에 즉시 반영됩니다.

컴포넌트에서 Props

Props는 부모 컴포넌트가 자식 컴포넌트에 데이터를 전달하는 방법입니다. Props는 읽기 전용이므로 자식 컴포넌트에서 이를 직접 변경할 수 없습니다.

// 자식 컴포넌트
const Greeting = ({ name }) => {
  return <h1>Hello, {name}!</h1>;
};

// 부모 컴포넌트
const App = () => {
  return (
    <div>
      {/* Props로 name 값을 자식 컴포넌트에 전달 */}
      <Greeting name="React" />
    </div>
  );
};

export default App;

 

 

  • Greeting 컴포넌트는 name이라는 props를 받으며, 이를 통해 인사말을 출력합니다.
  • 부모 컴포넌트인 App은 Greeting에 name="React"라는 값으로 props를 전달하고, 자식 컴포넌트는 이를 출력할 뿐 수정할 수 없습니다.

State와 Props의 차이점

  • State는 컴포넌트 내에서 관리되는 데이터로, 주로 사용자 상호작용을 처리할 때 사용됩니다.
  • Props는 부모에서 자식으로 전달되는 읽기 전용 데이터로, 컴포넌트 간 데이터 흐름을 관리하는 데 사용됩니다.

State와 Props를 함께 사용하는 예시

import React, { useState } from 'react';

// 자식 컴포넌트
const DisplayCount = ({ count }) => {
  return <h2>Current Count: {count}</h2>;
};

// 부모 컴포넌트
const CounterApp = () => {
  const [count, setCount] = useState(0);

  return (
    <div>
      <h1>Counter</h1>
      {/* State를 자식 컴포넌트로 전달 */}
      <DisplayCount count={count} />
      <button onClick={() => setCount(count + 1)}>Increment</button>
    </div>
  );
};

export default CounterApp;

 

 

 

패키지 매니저(Package Manager)

리액트로 개발을 하다보면 npm이라던가 yarn이라던가 하는 것들을 반드시 보게된다. 

얘들은 뭐길래 사용해야하는 걸까? 

 

이들은 패키지 매니저라고 불리는 것들이다. 말그대로 패키지를 관리하는 도구로서 프로젝트에서 라이브러리, 프레임워크, 모듈 등의 패키지를 관리한다. node.js에서는 흔히 npm과 yarn을 자주 사용한다. 이들은 웹사이트에서 패키지를 다운로드하여 컴퓨터에 설치를 해준다.

간단하게 이야기 하자면 설치된 프로그램을 관리하는 프로그램이다. 

 

그렇다면 node.js에서 가장 많이 사용되는 npm과 yarn은 어떤 차이를 가지고 있는 걸까?

NPM

npm은 node.js와 함께 2009년에 처음 출시된 패키지를 쉽게 설치하고 관리하기 위해 설계된 패키지 관리자이다.

명령어로는 다음과 같은 것들이 있다.

npm install <package>: 패키지 설치.
npm init: 새로운 프로젝트 초기화.
npm install 또는 npm i: package.json에 명시된 모든 패키지 설치.
npm update: 패키지 업데이트.

 

기능으로 package.json파일을 사용하여 프로젝트에 필요한 패키지와 그 버전을 관리한다. 기본적으로 중앙화된 npm레지스트리에서 패키지를 설치한다. 또 많은 패키지 관리와 배포에 사용된다. 

Yarn

yarn은 yet another resource negotiator의 약자로 2016년에 facebook이 npm의 성능과 보안 문제를 해결하기 위해 만든 패키지 관리자이다. npm과 호환이되며, 같은 기능을 제공하지만 성능과 안정성을 개선하려는 목적을 가지고 있다. 

 

명령어는 다음과 같다.

yarn add <package>: 패키지 설치 (npm의 install에 해당).
yarn init: 새로운 프로젝트 초기화.
yarn install: package.json 또는 yarn.lock 파일에 명시된 모든 패키지 설치.
yarn upgrade: 패키지 업데이트.

 

yarn 은 yarn.lock파일을 통해 설치된 모든 패키지의 정확한 버전을 고정하여 의존성 충돌을 방지하고 병렬설치 및 캐싱을 통해 빠른 속도록 패키지 설치가 가능하다. 오프라인 모드를 지원하고 이미 설치된 패키지를 네트워크 없이도 다시 설치가 가능하다 .

 

npm과 yarn의 차이점

출시일 2009년 2016년
속도 패키지 설치 속도가 느린 편 병렬 설치와 캐싱 덕분에 빠름
lock 파일 package-lock.json yarn.lock 파일을 사용하여 더 안정적인 의존성 관리
명령어 npm install, npm update 등 yarn add, yarn upgrade 등
보안 npm 자체적으로는 일부 보안 문제가 있었으나 최근 개선됨 Yarn은 npm보다 초기부터 보안성을 강조하여 더 안전함
오프라인 설치 제한적 네트워크 없이 오프라인 설치가 가능
커뮤니티 및 지원 매우 큰 커뮤니티와 지원 npm에 비해 작지만 꾸준히 성장하는 커뮤니티
패키지 해시 패키지 무결성 검사를 제공하지 않음 패키지의 해시 검사를 통해 무결성 보장

 

결론

  • npm: 기본적으로 Node.js와 함께 제공되며, 전 세계적으로 가장 많이 사용되는 패키지 관리자이다. 많은 프로젝트에서 안정적으로 사용되고 있으며, 최근 버전에서는 성능과 보안이 많이 개선되었다.
  • Yarn: npm의 성능과 보안 문제를 해결하기 위해 만들어진 대안으로, 특히 빠른 설치 속도와 의존성 관리에서 강점을 보인다. 대규모 프로젝트나 팀 협업에서 더 일관된 환경을 제공할 수 있다.

두 도구 모두 JavaScript 프로젝트에서 패키지 관리를 쉽게 해주지만, 프로젝트 규모나 속도에 민감하다면 Yarn을 사용하는 것이 유리할 수 있습니다. 하지만 둘 다 매우 유사하므로, 개인의 선호나 프로젝트 요구 사항에 따라 선택하면 된다.

과거 자바스크립트는 웹 브라우저에서 간단한 연산이나 시각적인 효과를 주는 단순한 스크립트 언어였습니다.

하지만, 현재는 웹 어플리케이션에서 가장 중요한 역할을 하고 있습니다. 

 

최근에는 수 많은 앱들이 자바스크립트를 활용하여 개발되고 있습니다. 하지만 대규모 앱을 개발하기 위해서 순수 자바스크립트만으로 개발하기에는 많은 한계점들이 존재합니다. 그래서 등장한 것이 REACT입니다.

 

REACT란?

리액트는 메타(구 페이스북)에서 개발한 오픈소스 자바스크립트 라이브러리입니다. SPA를 전제로 하고 있으며 Dirty Checking과 Virtual DOM을 활용하여 업데이트 해야하는 DOM요소를 찾아서 해당 부분만 업데이터 하기 때문에, 리렌더링이 잦은 동적인 모던 웹에서 매우 빠른 퍼포먼스를 내는게 가능합니다. 기본적으로 모듈형으로 개발되기 때문에 생산성 또한 상당히 높은 라이브러리라 순식간에 대세로 떠올랐습니다. 

 

REACT가 렌더링 최적화하는 방식 

리액트가 작동하는 방식는 어떤 데이터가 변할때마다 기존의 뷰를 날려버리고 처음부터 새로 렌더링하는 방식입니다. 이런 방식은 앱의 구조가 매우 간단하고 작성해야할 코드가 줄어든다는 장점이 있습니다.

 

그런데 웹 브라우저에서는 이런방식으로 하면 CPU점유율이 늘어나고 메모리 사용도 증가하면서 비효율적이지 않을까요?

 

이러한 방식에서 리액트가 성능을 아끼고 최적의 사용자 경험을 하는 비밀을 파악하려면 렌더링 방식에 대해서 알아야합니다.

Rendering

사용자 화면에서 view를 보여주는 것을 렌더링이라고 합니다. 

render()함수를 통해서 처음에 어떻게 보일지를 정합니다. 이 함수는 컴포넌트가 어떻게 생겼는지를 정의하는 역할을 하고 html 형식의 문자열을 반환하지 않고 뷰가 어떻게 생겼고 어떻게 작동하는지에 대한 정보를 지닌 객체를 반환합니다 .

새로운 컴포넌트가 들어가게 되면 컴포넌트들을 재귀적으로 렌더링 합니다. 

 

리액트에서는 뷰를 업데이트할 때 사실 조화과정을 거친다라는 표현을 써야합니다. 뷰를 새로운 요소로 갈아 끼우기 때문이죠.

갈아끼워진 뷰는 render함수가 반환하는 결과를 바로 DOM에 반영하지 않고 이전의 render함수가 만들었던 컴포넌트정보와 현재 render함수가 만든 컴포넌트 정보를 비교합니다. 

 

자바스크립트를 사용하여 두 가지 뷰를 최소한의 연산으로 비교한후, 둘의 차이를 알아내 최소한의 연산으로 업데이트 시키는 것이죠

 

DOM

dom은 document object model의 약자입니다. 객체로 문서 구조를 표현하는 방법으로 XML또는 html로 작성합니다.

DOM은 수 많은 플랫폼과 웹 브라우저에서 사용하는데 동적 UI에 최적화 되지 않는다는 문제를 가지고 있습니다. 이를 javascript를 통해 동적으로 만들어 주는 겁니다. 하지만 요즘 앱들의 규모가 커지면서 성능 이슈가 발생하고 있습니다. 

 

이러한 문제에 대한 해결책으로 virtualDOM이 등장합니다. 

 

Virtual DOM

virtual dom을 사용하면 실제 dom에 접근하여 조작하는 대신 이를 추상화한 자바스크립트 객체를 구성하여 사용합니다. 

리액트에서 웹브라우저에 실제 DOM을 업데이터 할때는 세가지 절차를 통합니다.

1. 데이터를 업데이트하면 전체 UI를 virtual DOM에 리렌더링 합니다.

2. 이전 virtual DOM에 있던 내용과 현재 내용을 비교합니다. 

3. 바뀐 부분만 실제 DOM에 적용합니다.

 

리액트  추가 특징 

리액트는 프레임워크가 아니라 라이브러리입니다. 프레임워크에서 MVC, MVW등의 구조를 지향하는 것과 달린 리액트는 뷰만 담당합니다. 

이러한 부분들에 대해서 router, axios,fetch, redux와 같은 다른 라이브러리들을 사용해서 보완할 수 있습니다.

+ Recent posts