리액트 스터디 보충수업 - React에 필요한 JS

Nomore·2025년 7월 30일

ReactStudy

목록 보기
4/7

React 개발을 위한 필수 JavaScript 문법

1. 변수 선언

JavaScript에서는 세 가지 방식으로 변수를 선언할 수 있습니다.

var (사용 권장하지 않음)

var name = 'John';
var name = 'Jane'; // 재선언 가능 (문제가 될 수 있음)

let (재할당 가능)

let age = 25;
age = 26; // 재할당 가능
// let age = 30; // 재선언 불가 (에러 발생)

const (재할당 불가)

const PI = 3.14159;
// PI = 3.14; // 재할당 불가 (에러 발생)

// 객체와 배열의 경우 내용은 수정 가능
const user = { name: 'John' };
user.name = 'Jane'; // 가능
user.age = 30; // 가능

2. 화살표 함수

기존 함수 표현식을 더 간결하게 작성할 수 있습니다.

기본 문법

// 기존 함수 표현식
const add = function(a, b) {
  return a + b;
};

// 화살표 함수
const add = (a, b) => {
  return a + b;
};

// 한 줄일 경우 중괄호와 return 생략 가능
const add = (a, b) => a + b;

// 매개변수가 하나일 경우 괄호 생략 가능
const double = x => x * 2;

// 매개변수가 없을 경우
const greet = () => console.log('Hello!');

React에서의 활용

// 이벤트 핸들러
<button onClick={() => console.log('Clicked!')}>
  Click me
</button>

// 컴포넌트 정의
const Button = () => {
  return <button>Click me</button>;
};

// 더 간결하게
const Button = () => <button>Click me</button>;

3. Import/Export

모듈을 불러오고 내보내는 방법입니다.

Named Export/Import

// math.js
export const add = (a, b) => a + b;
export const subtract = (a, b) => a - b;

// main.js
import { add, subtract } from './math.js';

Default Export/Import

// Button.js
const Button = () => <button>Click me</button>;
export default Button;

// App.js
import Button from './Button';

혼합 사용

// utils.js
export const formatDate = (date) => { /* ... */ };
export const formatTime = (time) => { /* ... */ };
const utils = { /* ... */ };
export default utils;

// main.js
import utils, { formatDate, formatTime } from './utils';

4. 기본 객체 조작

객체 생성과 접근

const user = {
  name: 'John',
  age: 30,
  email: 'john@example.com'
};

// 접근
console.log(user.name); // John
console.log(user['age']); // 30

// 수정
user.age = 31;
user['email'] = 'newemail@example.com';

// 추가
user.city = 'Seoul';

구조 분해 할당

const { name, age } = user;
console.log(name); // John

// 별칭 지정
const { name: userName, age: userAge } = user;

// 기본값 설정
const { city = 'Unknown' } = user;

스프레드 연산자

// 객체 복사
const newUser = { ...user };

// 객체 병합
const updatedUser = { ...user, age: 32, city: 'Busan' };

// 일부 속성 제외
const { age, ...userWithoutAge } = user;

5. 기본 배열 조작

배열 생성과 접근

const fruits = ['apple', 'banana', 'orange'];
console.log(fruits[0]); // apple
console.log(fruits.length); // 3

주요 배열 메서드

// push: 끝에 추가
fruits.push('grape');

// pop: 끝에서 제거
const lastFruit = fruits.pop();

// map: 변환된 새 배열 생성
const upperFruits = fruits.map(fruit => fruit.toUpperCase());

// filter: 조건에 맞는 요소만 선택
const longFruits = fruits.filter(fruit => fruit.length > 5);

// find: 조건에 맞는 첫 번째 요소
const foundFruit = fruits.find(fruit => fruit.startsWith('b'));

// reduce: 누적 계산
const total = [1, 2, 3, 4].reduce((sum, num) => sum + num, 0);

스프레드 연산자

// 배열 복사
const newFruits = [...fruits];

// 배열 병합
const moreFruits = [...fruits, 'mango', 'kiwi'];
const allFruits = [...fruits, ...moreFruits];

// 배열 구조 분해
const [first, second, ...rest] = fruits;

6. 삼항 연산자

조건에 따라 다른 값을 반환하는 간단한 조건문입니다.

// 기본 문법: 조건 ? 참일때값 : 거짓일때값
const age = 20;
const status = age >= 18 ? '성인' : '미성년자';

// React에서의 활용
const Component = () => {
  const isLoggedIn = true;
  
  return (
    <div>
      {isLoggedIn ? <WelcomeMessage /> : <LoginButton />}
    </div>
  );
};

// 중첩 사용 (가독성 주의)
const grade = score >= 90 ? 'A' :
             score >= 80 ? 'B' :
             score >= 70 ? 'C' : 'F';

7. 조건부 렌더링 연산자

자바스크립트의 Falsy 개념

자바스크립트에서 falsy는 조건식에서 false로 평가되는 값들을 의미합니다. 조건문(if, while 등)이나 논리 연산에서 false처럼 동작하는 값들로, 아래 6가지로 고정되어 있습니다:

  1. false
  2. 0 (숫자 0)
  3. "" (빈 문자열)
  4. null
  5. undefined
  6. NaN

특징

falsy 값은 조건문에서 false로 간주되지만, === (엄격한 동등 비교)로는 false와 동일하지 않습니다.
예: if (0) { ... } → 실행되지 않음, 하지만 0 === falsefalse 반환.

falsy가 아닌 값은 truthy로 간주되어 조건문에서 true처럼 동작합니다.

예제

if (0) {
  console.log("이건 실행 안 됨"); // falsy 값
} else {
  console.log("0은 falsy"); // 실행됨
}

if ("hello") {
  console.log("이건 실행됨"); // truthy 값
}

falsy 값은 6개로 한정되어 있으니 암기하세요.
falsy 값을 조건문에 넣어 동작을 확인해보세요.
!! 연산자(이중 부정)를 사용하면 값이 truthy인지 falsy인지 빠르게 확인 가능:
!!0 → false
!!"hello"true

&& 연산자

조건이 참일 때만 렌더링합니다.

// 기본 원리: 앞이 true면 뒤를 반환, false면 false 반환
const result1 = true && 'Hello'; // 'Hello'
const result2 = false && 'Hello'; // false

// React에서의 활용
const Component = () => {
  const showMessage = true;
  const items = ['item1', 'item2'];
  
  return (
    <div>
      {showMessage && <p>메시지를 표시합니다</p>}
      {items.length > 0 && <p>아이템이 있습니다</p>}
    </div>
  );
};

// 주의사항: 0은 falsy지만 렌더링됨
const count = 0;
return <div>{count && <p>카운트: {count}</p>}</div>; // 0이 렌더링됨
// 해결방법
return <div>{count > 0 && <p>카운트: {count}</p>}</div>;

|| 연산자

기본값을 제공할 때 사용합니다.

// 기본 원리: 앞이 falsy면 뒤를 반환
const name = userName || 'Guest';

// React에서의 활용
const Component = ({ title }) => {
  return <h1>{title || '기본 제목'}</h1>;
};

8. 배열의 map을 이용한 리스트 렌더링

React에서 배열 데이터를 화면에 표시할 때 가장 많이 사용하는 패턴입니다.

기본 사용법

const TodoList = () => {
  const todos = [
    { id: 1, text: 'React 공부하기' },
    { id: 2, text: 'JavaScript 복습하기' },
    { id: 3, text: '프로젝트 만들기' }
  ];
  
  return (
    <ul>
      {todos.map(todo => (
        <li key={todo.id}>{todo.text}</li>
      ))}
    </ul>
  );
};

조건부 렌더링과 함께 사용

const UserList = () => {
  const users = [
    { id: 1, name: 'John', isActive: true },
    { id: 2, name: 'Jane', isActive: false },
    { id: 3, name: 'Bob', isActive: true }
  ];
  
  return (
    <div>
      <h2>활성 사용자</h2>
      <ul>
        {users
          .filter(user => user.isActive)
          .map(user => (
            <li key={user.id}>{user.name}</li>
          ))}
      </ul>
    </div>
  );
};

복잡한 컴포넌트 렌더링

const ProductList = () => {
  const products = [
    { id: 1, name: '노트북', price: 1500000, inStock: true },
    { id: 2, name: '마우스', price: 30000, inStock: false },
    { id: 3, name: '키보드', price: 100000, inStock: true }
  ];
  
  return (
    <div className="product-list">
      {products.map(product => (
        <div key={product.id} className="product-card">
          <h3>{product.name}</h3>
          <p>가격: {product.price.toLocaleString()}</p>
          {product.inStock ? (
            <button>구매하기</button>
          ) : (
            <p style={{ color: 'red' }}>품절</p>
          )}
        </div>
      ))}
    </div>
  );
};

key의 중요성

// 잘못된 예: index를 key로 사용
{items.map((item, index) => (
  <li key={index}>{item}</li> // 순서가 바뀔 수 있는 경우 문제 발생
))}

// 올바른 예: 고유한 id 사용
{items.map(item => (
  <li key={item.id}>{item.name}</li>
))}

Todo List

import { useState } from 'react';

// TodoItem 컴포넌트
const TodoItem = ({ todo, onToggle, onDelete }) => {
  return (
    <li style={{
      display: 'flex',
      alignItems: 'center',
      padding: '10px',
      borderBottom: '1px solid #eee',
      textDecoration: todo.completed ? 'line-through' : 'none',
      color: todo.completed ? '#999' : '#333'
    }}>
      <input
        type="checkbox"
        checked={todo.completed}
        onChange={() => onToggle(todo.id)}
        style={{ marginRight: '10px' }}
      />
      <span style={{ flex: 1 }}>{todo.text}</span>
      <button
        onClick={() => onDelete(todo.id)}
        style={{
          padding: '5px 10px',
          backgroundColor: '#ff4444',
          color: 'white',
          border: 'none',
          borderRadius: '4px',
          cursor: 'pointer'
        }}
      >
        삭제
      </button>
    </li>
  );
};

// App 컴포넌트
const App = () => {
  // 상태 관리 - const 사용
  const [todos, setTodos] = useState([
    { id: 1, text: 'React 공부하기', completed: false },
    { id: 2, text: 'JavaScript 복습하기', completed: true },
    { id: 3, text: '프로젝트 만들기', completed: false }
  ]);
  const [inputValue, setInputValue] = useState('');

  // 할 일 추가 - 화살표 함수 사용
  const addTodo = () => {
    if (inputValue.trim() === '') return;
    
    const newTodo = {
      id: Date.now(),
      text: inputValue,
      completed: false
    };
    
    // 스프레드 연산자로 배열 복사 및 추가
    setTodos([...todos, newTodo]);
    setInputValue('');
  };

  // 할 일 완료 토글
  const toggleTodo = (id) => {
    // map을 사용한 배열 조작
    setTodos(todos.map(todo => 
      todo.id === id 
        ? { ...todo, completed: !todo.completed } // 스프레드 연산자로 객체 복사
        : todo
    ));
  };

  // 할 일 삭제
  const deleteTodo = (id) => {
    // filter를 사용한 배열 조작
    setTodos(todos.filter(todo => todo.id !== id));
  };

  // Enter 키 입력 처리
  const handleKeyPress = (e) => {
    if (e.key === 'Enter') {
      addTodo();
    }
  };

  // 완료된 할 일 개수 계산
  const completedCount = todos.filter(todo => todo.completed).length;

  return (
    <div style={{
      maxWidth: '600px',
      margin: '50px auto',
      padding: '20px',
      backgroundColor: '#f5f5f5',
      borderRadius: '8px',
      boxShadow: '0 2px 10px rgba(0,0,0,0.1)'
    }}>
      <h1 style={{ textAlign: 'center', color: '#333' }}>📝 Todo List</h1>
      
      {/* 입력 영역 */}
      <div style={{ display: 'flex', marginBottom: '20px' }}>
        <input
          type="text"
          value={inputValue}
          onChange={(e) => setInputValue(e.target.value)}
          onKeyPress={handleKeyPress}
          placeholder="할 일을 입력하세요..."
          style={{
            flex: 1,
            padding: '10px',
            fontSize: '16px',
            border: '1px solid #ddd',
            borderRadius: '4px 0 0 4px'
          }}
        />
        <button
          onClick={addTodo}
          style={{
            padding: '10px 20px',
            backgroundColor: '#4CAF50',
            color: 'white',
            border: 'none',
            borderRadius: '0 4px 4px 0',
            cursor: 'pointer',
            fontSize: '16px'
          }}
        >
          추가
        </button>
      </div>

      {/* 통계 - 조건부 렌더링 && 사용 */}
      {todos.length > 0 && (
        <div style={{ 
          marginBottom: '10px', 
          textAlign: 'center',
          color: '#666'
        }}>
          완료: {completedCount} / 전체: {todos.length}
        </div>
      )}

      {/* Todo 리스트 - map을 사용한 리스트 렌더링 */}
      <ul style={{ 
        listStyle: 'none', 
        padding: 0,
        backgroundColor: 'white',
        borderRadius: '4px'
      }}>
        {todos.length === 0 ? (
          <li style={{ 
            textAlign: 'center', 
            padding: '20px',
            color: '#999' 
          }}>
            할 일이 없습니다. 새로운 할 일을 추가해주세요!
          </li>
        ) : (
          todos.map(todo => (
            <TodoItem
              key={todo.id}
              todo={todo}
              onToggle={toggleTodo}
              onDelete={deleteTodo}
            />
          ))
        )}
      </ul>

      {/* 전체 삭제 버튼 - 조건부 렌더링 */}
      {todos.length > 0 && completedCount === todos.length && (
        <button
          onClick={() => setTodos([])}
          style={{
            width: '100%',
            marginTop: '20px',
            padding: '10px',
            backgroundColor: '#ff9800',
            color: 'white',
            border: 'none',
            borderRadius: '4px',
            cursor: 'pointer',
            fontSize: '16px'
          }}
        >
          모두 삭제
        </button>
      )}
    </div>
  );
};

export default App;

0개의 댓글