반응형

리액트 이벤트 처리 마스터하기 🎯:
초보자를 위한 실전 가이드

"onClick도 잘 모르겠는데, 이벤트 객체? this 바인딩?" 🤯
리액트 초보가 가장 헷갈리는 바로 그 주제!
이제는 명쾌하게 정리해드릴게요.
반응형

 

안녕하세요, 여러분! 😊

리액트를 처음 접하고 나서 가장 많이 헤매는 부분 중 하나가 바로 "이벤트 처리" 아닐까요?

JavaScript에서는 잘 되던 이벤트 등록이, 리액트에선 갑자기 JSX 문법이니 이벤트 객체니 하면서 헷갈리는 경우 많죠.

이번 글에서는 리액트의 이벤트 시스템을 차근차근 정리하고, 자주 실수하는 포인트도 함께 짚어드릴게요.

실습 위주로 따라오시면, 글 다 읽고 나면 자신 있게 "이벤트 처리? 그건 이제 껌이죠!"라고 말하실 수 있을 거예요.

그럼 시작해볼까요? 😊

 

1. 리액트의 기본 이벤트 바인딩 방식 🧷

리액트에서 이벤트를 다루는 방식은 일반 HTML과 다소 다릅니다.

JSX 문법을 따르기 때문에 이벤트 속성을 onclick이 아닌 onClick처럼 카멜케이스(camelCase)로 작성해야 하고, 실행할 함수는 문자열이 아닌 함수 참조로 전달해야 해요.

예제 코드 👇

function ClickButton() {
  function handleClick() {
    alert('버튼이 클릭되었어요!');
  }

  return (
    <button onClick={handleClick}>
      클릭해보세요!
    </button>
  );
}

 

위 코드에서 onClick={handleClick}처럼 함수 이름만 넘기고 괄호는 붙이지 않죠?

괄호를 붙이면 컴포넌트 렌더링 시점에 함수가 실행돼버리니까 꼭 조심해야 해요!

초보자 주의 포인트 📌

  • JSX에서는 이벤트 이름을 반드시 카멜케이스로 작성해야 합니다.
  • 이벤트 핸들러는 함수 참조로 전달해야 하며, 함수 호출문이 들어가면 안 돼요!

잘못된 예와 올바른 예 🆚

잘못된 방식 올바른 방식
onclick="handleClick()" onClick={handleClick}
onClick={handleClick()} onClick={() => handleClick()}

익명 함수로 감싸는 방식은 상황에 따라 사용하면 유용하지만, 함수 내부에서 props나 state를 다뤄야 할 때만 사용하는 게 좋아요!

 

 

2. SyntheticEvent란? 🤖 진짜 이벤트 객체와의 차이

리액트에서 이벤트를 처리할 때 SyntheticEvent라는 개념이 등장해요.

"이게 뭐야?" 싶으실 텐데, 쉽게 말해 브라우저의 native event를 감싼 리액트 전용 래퍼 객체라고 생각하시면 됩니다.

왜 SyntheticEvent를 쓸까?

  • 브라우저 간 이벤트 처리 방식이 달라서, 이를 통합하기 위해 리액트가 만든 표준화된 이벤트 객체예요.
  • stopPropagation()이나 preventDefault() 같은 메서드도 동일하게 사용할 수 있어요.

SyntheticEvent의 특징 🌐

기능 설명
통합 API 브라우저마다 다른 이벤트 처리 방식을 하나의 인터페이스로 제공
자동 메모리 최적화 이벤트 객체는 자동으로 풀(pool)에 반환되어 재사용됨
비동기 코드에서의 문제 setTimeout 등에서 접근하면 이벤트 객체가 null로 초기화되어 있음

Tip! 이벤트 객체를 비동기적으로 사용해야 할 경우 e.persist()를 꼭 호출하세요!

예시 코드 👇

function LogEvent() {
  function handleClick(e) {
    e.persist(); // SyntheticEvent 풀 반환 방지
    setTimeout(() => {
      console.log(e.type); // 'click'
    }, 1000);
  }

  return (
    <button onClick={handleClick}>이벤트 타입 출력</button>
  );
}

 

이처럼 SyntheticEvent는 리액트의 퍼포먼스 최적화를 위한 핵심 설계 중 하나이며, JS 이벤트와 매우 유사하지만 React 내부에서 관리된다는 점을 꼭 기억해 주세요!

 

 

3. 이벤트 객체 다루기 – e.preventDefault(), e.target 등 🧪

리액트 이벤트 핸들러에서 전달받는 e는 앞서 배운 SyntheticEvent 객체입니다.

이 객체를 활용하면 preventDefault()로 기본 동작을 막거나, e.target을 통해 이벤트가 발생한 요소에 접근할 수 있어요.

📌 주요 이벤트 객체 프로퍼티/메서드 요약

속성/메서드 설명
e.preventDefault() 기본 동작(예: 폼 제출, 링크 이동 등)을 막습니다.
e.stopPropagation() 이벤트 버블링(상위 요소로 전파)을 막습니다.
e.target 이벤트가 실제로 발생한 DOM 요소를 가리킵니다.
e.currentTarget 이벤트 핸들러가 바인딩된 요소를 가리킵니다.

🎯 예제 1 – 기본 동작 막기 (preventDefault)

function FormExample() {
  function handleSubmit(e) {
    e.preventDefault();
    alert('폼 제출이 막혔어요!');
  }

  return (
    <form onSubmit={handleSubmit}>
      <input type="text" placeholder="이름 입력" />
      <button type="submit">제출</button>
    </form>
  );
}

 

이 예제는 기본적으로 페이지 리로드를 막고, alert만 출력해요.

만약 e.preventDefault()를 생략하면 폼이 제출되면서 페이지가 새로고침됩니다.

🎯 예제 2 – target과 currentTarget 구분

function TargetExample() {
  function handleClick(e) {
    console.log('target:', e.target.tagName);
    console.log('currentTarget:', e.currentTarget.tagName);
  }

  return (
    <div onClick={handleClick} style={{ padding: '1em', border: '1px solid #ccc' }}>
      <button>버튼 클릭!</button>
    </div>
  );
}

 

이 코드에서 e.target실제 클릭된 버튼이고,

e.currentTargetonClick이 바인딩된 div를 가리킵니다.

DOM 이벤트 흐름을 이해할 때 아주 중요한 개념이에요!

 

💡 정리!

이벤트 객체를 잘 활용하면 복잡한 사용자 상호작용도 깔끔하게 구현할 수 있어요.

특히 targetcurrentTarget의 차이는 반드시 숙지해두세요!

 

 

4. this 바인딩 문제 해결법과 화살표 함수 사용법 🧭

리액트에서 이벤트 핸들러를 작성하다 보면 thisundefined가 되는 문제가 발생하곤 해요.

특히 클래스형 컴포넌트를 사용할 때 this.handleClick처럼 핸들러를 호출하려고 하면 에러가 발생할 수 있죠.

🎯 클래스형 컴포넌트에서의 this 바인딩

class EventExample extends React.Component {
  constructor(props) {
    super(props);
    this.state = { count: 0 };
    this.handleClick = this.handleClick.bind(this); // 바인딩 필수
  }

  handleClick() {
    this.setState({ count: this.state.count + 1 });
  }

  render() {
    return (
      <button onClick={this.handleClick}>
        클릭 횟수: {this.state.count}
      </button>
    );
  }
}

 

this.handleClick을 이벤트에 넘기기 전에 생성자에서 바인딩을 꼭 해줘야 해요.

그렇지 않으면 this가 undefined가 되어 this.setState가 작동하지 않아요!

💡 화살표 함수로 해결하기

class ArrowEventExample extends React.Component {
  state = { count: 0 };

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

  render() {
    return (
      <button onClick={this.handleClick}>
        클릭 횟수: {this.state.count}
      </button>
    );
  }
}

 

handleClick화살표 함수로 선언하면 생성자에서 바인딩하지 않아도 this가 자동으로 바인딩돼요.

요즘은 이 방식이 더 보편적으로 쓰이고 있어요!

🚀 함수형 컴포넌트에서는?

함수형 컴포넌트에서는 this를 쓰지 않기 때문에 바인딩 문제가 아예 없습니다!

대신 useState, useCallback 등 훅을 적절히 활용해서 상태나 함수를 관리하면 돼요.

function FunctionalEvent() {
  const [count, setCount] = React.useState(0);

  const handleClick = () => {
    setCount(prev => prev + 1);
  };

  return (
    <button onClick={handleClick}>
      클릭 횟수: {count}
    </button>
  );
}

 

🔥 핵심 요약:

클래스형 컴포넌트는 this 바인딩이 필수!

하지만 함수형 컴포넌트에서는 바인딩 걱정 없이 깔끔하게 이벤트를 처리할 수 있어요.

이제 this 때문에 헤맬 일은 없겠죠?

 

 

5. 커스텀 이벤트 핸들러 구조 설계하기 🛠️

리액트에서 이벤트 처리는 단순히 onClick={() => ...}처럼 인라인으로 끝낼 수도 있지만, 규모가 커질수록 이벤트 핸들러의 구조화가 굉장히 중요해집니다.

컴포넌트가 많아지고 이벤트가 늘어나면 로직 재사용유지보수에 큰 차이를 만들어요.

💡 커스텀 핸들러를 분리하는 이유

  • 핸들러 로직이 복잡해질 경우 읽기 어려운 JSX가 되기 쉽습니다.
  • 여러 컴포넌트에서 같은 로직을 재사용할 때 중복을 피할 수 있습니다.

🧩 예제: 입력 값 검증 핸들러

function useInputValidator() {
  const validate = (value) => {
    if (value.length < 3) {
      return "3자 이상 입력해주세요!";
    }
    return "";
  };

  return validate;
}

function InputForm() {
  const [input, setInput] = React.useState("");
  const [error, setError] = React.useState("");
  const validate = useInputValidator();

  const handleChange = (e) => {
    const val = e.target.value;
    setInput(val);
    setError(validate(val));
  };

  return (
    <div>
      <input value={input} onChange={handleChange} />
      {error && <p style={{ color: "red" }}>{error}</p>}
    </div>
  );
}

 

위 코드에서는 useInputValidator라는 커스텀 훅으로 입력값 검증 로직을 분리했어요.

핸들러 함수는 깔끔하게 유지되고, 검증 로직은 재사용 가능합니다.

⚙️ 다양한 상황에 맞는 이벤트 구조 설계 팁

상황 설계 팁
반복적인 입력 검증 커스텀 훅으로 검증 함수 모듈화
복잡한 버튼 액션 핸들러에서 여러 유틸 함수를 조합
다양한 트리거로 같은 동작 공통 로직을 별도 핸들러로 추출

🚀 요약!

리액트 이벤트 처리를 고수처럼 만들고 싶다면, 핸들러를 짧고 간결하게 유지하고, 복잡한 로직은 커스텀 훅이나 함수로 분리해보세요.

유지보수가 10배 쉬워집니다!

 

 

6. 예제로 알아보는 이벤트 처리 베스트 프랙티스 🧠

리액트의 이벤트 시스템을 이론적으로 이해하는 것도 중요하지만, 예제를 통해 감을 잡는 것이 훨씬 효과적입니다.

이번에는 실무에서 자주 등장하는 이벤트 시나리오 3가지와 그에 맞는 처리 방법을 살펴보겠습니다.

🧪 예제 1: 인풋 실시간 반영 + 엔터키 제출

function ChatInput() {
  const [message, setMessage] = React.useState("");

  const handleChange = (e) => setMessage(e.target.value);

  const handleKeyDown = (e) => {
    if (e.key === 'Enter' && !e.shiftKey) {
      e.preventDefault();
      alert(`메시지 전송: ${message}`);
      setMessage("");
    }
  };

  return (
    <textarea
      value={message}
      onChange={handleChange}
      onKeyDown={handleKeyDown}
      placeholder="메시지를 입력하고 Enter를 눌러보세요"
    />
  );
}

 

이벤트를 중첩 조건으로 처리할 수 있고, preventDefault로 기본 동작을 제어할 수 있다는 것을 보여주는 예제예요.

🧪 예제 2: 동적으로 생성된 리스트 항목 클릭

function DynamicList() {
  const items = ["사과", "바나나", "오렌지"];

  const handleClick = (item) => {
    alert(`${item}을 선택하셨습니다!`);
  };

  return (
    <ul>
      {items.map((item, idx) => (
        <li key={idx} onClick={() => handleClick(item)}>
          {item}
        </li>
      ))}
    </ul>
  );
}

 

이벤트 핸들러에 익명 함수를 사용해서 인자를 넘기는 패턴이에요.

많이 쓰이는 패턴이지만 렌더링 성능에 영향을 줄 수 있으니 상황에 따라 useCallback도 고려해보세요!

🧪 예제 3: 부모에서 자식 이벤트 제어

function ParentComponent() {
  const handleChildClick = (msg) => {
    console.log("자식 클릭됨:", msg);
  };

  return <ChildComponent onCustomClick={handleChildClick} />;
}

function ChildComponent({ onCustomClick }) {
  return (
    <button onClick={() => onCustomClick("Hello from Child")}>
      자식 버튼
    </button>
  );
}

 

부모가 자식의 이벤트를 제어할 수 있도록 props로 핸들러를 전달하는 전형적인 패턴입니다.

컴포넌트 간 상호작용을 설계할 때 자주 사용되죠.

✅ 실전에서 꼭 기억할 베스트 프랙티스

  • 이벤트 핸들러는 짧고 명확하게 구성하자.
  • 중복되는 로직은 커스텀 훅이나 외부 함수로 분리하자.
  • props로 이벤트를 넘길 때는 이름을 명확하게 지정하자 (onClick보다는 onItemSelect 등).

🎉 결론!

이벤트 처리를 잘하면 리액트 앱의 UX, 유지보수, 성능까지 모두 향상됩니다.

여기까지 익히셨다면 이제 실전에서도 막힘 없이 이벤트를 자유자재로 다루실 수 있어요!

 

🔚 이벤트, 이제는 두렵지 않다!

여기까지 따라오시느라 정말 수고 많으셨어요!

처음엔 onClick 하나도 헷갈리던 리액트 이벤트 처리 방식, 이제는 꽤나 자신감이 붙지 않으셨나요?

이번 글을 통해 이벤트 객체의 구조부터, this 바인딩 문제, 커스텀 핸들러 설계, 실전 예제까지 모두 다뤄봤습니다.

앞으로는 이벤트 처리 코드를 짤 때,

"이걸 함수로 빼면 더 깔끔할까?",

"preventDefault 꼭 써야 할까?"

같은 고민을 스스로 던지고 답할 수 있게 되실 거예요.

 

다음엔 폼 처리, 상태 동기화 등 더 깊은 리액트 주제로 다시 찾아올게요.

놓치지 않도록 구독이나 북마크 해두시는 거 잊지 마시구요! 😊

반응형
반응형

리액트 훅 완전정복! 초보자를 위한 주요 훅 사용법 가이드

리액트 초보자라면 꼭 알아야 할 필수 훅들!
직접 써보고 '와, 이게 진짜 편하네' 했던
그 기능들만 모았습니다.
반응형

 

안녕하세요!

리액트를 공부하다 보면 꼭 만나게 되는 개념 중 하나가 바로 "훅(Hook)"이죠. 함수형 컴포넌트 안에서 상태를 관리하거나 생명주기 효과를 처리할 때 빠질 수 없는 친구인데요.

이번 글에서는 useState, useEffect, useRef, useMemo, useCallback, useContext 등 자주 사용되는 훅들을 초보자 눈높이에 맞춰 쉽게 설명해드릴게요.

직접 써보며 헷갈렸던 부분까지 모두 정리했으니 끝까지 함께 해주세요!

 

1. useState 🧠 상태를 다루는 가장 기본 훅

useState는 리액트 훅 중에서 가장 자주 사용되는 기능 중 하나예요.

컴포넌트 안에서 변하는 값, 예를 들면 버튼 클릭 횟수나 입력 필드의 내용 등을 저장하고 관리하는 데 사용됩니다.

기존의 클래스형 컴포넌트에서 this.statethis.setState()를 사용하던 것을 함수형 컴포넌트에서 더 간단하게 구현할 수 있게 해주는 거죠!

📌 기본 문법

const [상태변수, 상태변경함수] = useState(초기값);

🧪 실습 예제

import { useState } from 'react';

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

  return (
    <div>
      <p>현재 카운트: {count}</p>
      <button onClick={() => setCount(count + 1)}>증가</button>
    </div>
  );
}

💡 사용 시 팁

  • 여러 개의 상태가 필요한 경우, useState를 여러 번 선언해도 전혀 문제 없어요!
  • 상태가 바뀌면 해당 컴포넌트는 자동으로 다시 렌더링돼요. 이걸 잘 활용해야 렌더링 최적화에 도움이 돼요.

📋 요약 정리

항목 내용
사용 목적 컴포넌트 내 상태값 관리
초기값 숫자, 문자열, 객체 등 다양한 자료형 가능
업데이트 방식 전용 함수(set함수)를 통해 변경

처음 리액트를 접하면 상태라는 개념이 어렵게 느껴질 수도 있지만, useState만 잘 이해해도 리액트 앱의 절반은 완성한 셈이에요!

 

다음은 useEffect를 통해 컴포넌트의 생명주기를 어떻게 다룰 수 있는지 알아볼게요.

 

 

2. useEffect 🔁 생명주기 대체 훅

리액트 함수형 컴포넌트는 생명주기 메서드가 없죠.

그 대신 useEffect를 사용하면 componentDidMount, componentDidUpdate, componentWillUnmount처럼 동작하게 만들 수 있어요.

 

예를 들어,

컴포넌트가 처음 화면에 나타날 때 데이터를 불러온다거나, 특정 값이 바뀔 때마다 API 요청을 보낸다거나, 또는 컴포넌트가 사라질 때 정리 작업(clean-up)을 해야 할 때 아주 유용합니다.

📌 기본 문법

useEffect(() => {
  // 실행할 코드
  return () => {
    // 정리(clean-up) 코드
  };
}, [의존성 배열]);

🧪 실습 예제

import { useEffect, useState } from 'react';

function Timer() {
  const [seconds, setSeconds] = useState(0);

  useEffect(() => {
    const interval = setInterval(() => {
      setSeconds(prev => prev + 1);
    }, 1000);

    return () => {
      clearInterval(interval);
    };
  }, []);

  return <p>{seconds}초 지났습니다.</p>;
}

💡 동작 방식에 따른 분기

의존성 배열 실행 시점
[] 컴포넌트 최초 렌더링 시 한 번만
[count] count 값이 바뀔 때마다 실행
의존성 없음 모든 렌더링마다 실행

📣 주의할 점

  • 의존성 배열을 제대로 설정하지 않으면 무한 루프에 빠질 수 있어요!
  • 정리(clean-up) 함수는 컴포넌트가 사라질 때뿐만 아니라 다음 실행 전에 호출되므로 유의해야 해요.

이제 useEffect를 통해 비동기 작업이나 외부 이벤트도 자유롭게 처리할 수 있어요.

 

다음으로는, DOM을 직접 제어하거나 변경되지 않는 값을 저장할 수 있는 useRef를 알아보러 가볼까요?

 

 

3. useRef 🔍 DOM 제어와 값 기억에 강력

useRef는 리액트에서 아주 유용하지만, 처음엔 그 쓰임새가 헷갈릴 수 있는 훅이에요.

주로 DOM 요소를 직접 제어하거나 렌더링 사이에도 유지되어야 하는 값을 저장할 때 사용해요.

이 훅의 특징은 값을 변경해도 컴포넌트를 다시 렌더링하지 않는다는 거예요.

그래서 카운트 같은 상태는 useState를 쓰지만, 변경되더라도 화면에 영향을 주지 않아야 하는 값은 useRef를 쓰는 게 더 적절해요.

📌 기본 문법

const myRef = useRef(초기값);

// 접근: myRef.current

🧪 DOM 조작 예제

import { useRef } from 'react';

function FocusInput() {
  const inputRef = useRef();

  const handleClick = () => {
    inputRef.current.focus();
  };

  return (
    <div>
      <input ref={inputRef} type="text" />
      <button onClick={handleClick}>포커스 주기</button>
    </div>
  );
}

🧠 상태 저장 예제 (렌더링 방지)

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

function RenderCount() {
  const [value, setValue] = useState('');
  const count = useRef(1);

  useEffect(() => {
    count.current += 1;
  });

  return (
    <div>
      <input value={value} onChange={(e) => setValue(e.target.value)} />
      <p>렌더링 횟수: {count.current}</p>
    </div>
  );
}

📋 요약 정리

기능 설명
DOM 접근 ref로 지정한 요소에 직접 접근 가능
렌더링 영향 없음 값 변경 시 컴포넌트 재렌더링 발생 안 함
유지되는 값 렌더링 사이에 값을 유지하고 싶을 때 사용

useRef는 보이지 않는 메모장 같아요.

화면에 영향을 주지 않으면서 내부 데이터를 보관할 수 있거든요.

 

이 다음엔 계산이 무거운 작업을 최적화하는 useMemo로 넘어가볼게요!

 

 

4. useMemo 📦 계산 결과를 메모이제이션

리액트에서 어떤 연산이 너무 자주 반복되면, 앱이 느려지는 원인이 될 수 있어요.

useMemo는 이런 상황에서 비용이 많이 드는 계산을 기억해두고, 필요할 때만 다시 계산하게 도와줘요.

쉽게 말해서,

어떤 계산된 값이 매번 바뀌지 않는데도 계속 다시 계산되는 게 비효율적이라면,

useMemo로 감싸서 “얘는 값 안 바뀌면 계산 다시 안 해!”라고 알려주는 거예요.

📌 기본 문법

const memoizedValue = useMemo(() => {
  return 복잡한계산(입력값);
}, [의존성값]);

🧪 실습 예제

import { useState, useMemo } from 'react';

function ExpensiveComponent() {
  const [number, setNumber] = useState(1);
  const [text, setText] = useState('');

  const factorial = (n) => {
    console.log('계산 중...');
    return n <= 1 ? 1 : n * factorial(n - 1);
  };

  const memoizedFactorial = useMemo(() => factorial(number), [number]);

  return (
    <div>
      <p>{number}! = {memoizedFactorial}</p>
      <button onClick={() => setNumber(prev => prev + 1)}>증가</button>
      <input value={text} onChange={(e) => setText(e.target.value)} />
    </div>
  );
}

💡 사용 시 주의할 점

  • 무조건 쓰면 성능이 좋아지진 않아요! 진짜 계산이 무거운 경우에만 쓰는 게 좋아요.
  • 의존성 배열을 잘못 설정하면 오래된 값을 계속 사용하게 될 수도 있어요.

📋 요약 정리

기능 설명
계산 결과 캐싱 의존값이 변하지 않으면 이전 계산 결과 재사용
최적화 대상 복잡하거나 반복되는 계산 작업
렌더링 영향 렌더링마다 계산 방지 가능

정말 계산이 무거운 로직에만 쓰는 게 핵심이에요.

불필요한 useMemo 남용은 오히려 독!

 

다음은 콜백 함수 자체를 메모이제이션해서 불필요한 재렌더링을 막는 useCallback 훅을 알아봅시다!

 

 

5. useCallback 🔄 콜백 함수를 캐싱

useCallbackuseMemo와 비슷하지만, 함수 자체를 기억하는 데 초점을 맞춘 훅이에요.

특히 하위 컴포넌트에 함수를 props로 넘길 때 불필요한 재렌더링을 막기 위해 많이 사용돼요.

리액트에서는 함수도 매번 새로 만들어지는 값이에요.

그래서 의도치 않게 하위 컴포넌트가 렌더링 될 필요가 없어도 재렌더링되곤 해요.

이때 useCallback을 사용하면 함수를 고정시켜서 그런 현상을 막을 수 있어요.

📌 기본 문법

const memoizedCallback = useCallback(() => {
  함수내용
}, [의존성값]);

🧪 실습 예제

import { useState, useCallback } from 'react';

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

  const handleClick = useCallback(() => {
    console.log('버튼 클릭!');
  }, []);

  return (
    <div>
      <p>카운트: {count}</p>
      <button onClick={() => setCount(prev => prev + 1)}>증가</button>
      <Child onClick={handleClick} />
    </div>
  );
}

function Child({ onClick }) {
  console.log('Child 렌더링');
  return <button onClick={onClick}>클릭</button>;
}

💡 언제 써야 할까?

  • 하위 컴포넌트가 React.memo를 사용 중일 때, props로 넘기는 함수가 매번 바뀌면 재렌더링이 발생해요.
  • useCallback을 쓰면 함수 참조가 고정돼서 렌더링 최적화가 가능해져요.

📋 요약 정리

기능 설명
함수 메모이제이션 렌더링 시 동일한 함수 참조 유지
최적화 대상 React.memo와 함께 하위 컴포넌트 렌더링 방지
주의사항 의존성 배열 정확히 지정해야 효과 있음

useCallback은 성능 최적화를 위한 작은 도구예요.

얘를 잘 쓰면 쓸수록 렌더링 횟수를 줄여 앱이 쾌적해집니다.

 

다음은 컴포넌트 간 전역 상태를 쉽게 공유할 수 있는 useContext를 살펴볼 차례예요!

 

 

6. useContext 🌐 전역 상태를 간단하게 공유

리액트 앱이 커지다 보면 여러 컴포넌트에서 공통된 데이터를 사용해야 하는 상황이 많아져요.

예를 들어 사용자 정보, 테마 설정, 로그인 상태 등은 전역 상태로 관리하는 게 편하죠.

이럴 때 사용하는 게 useContext입니다.

useContext는 컴포넌트 트리 전반에 걸쳐 값을 전달하는 React Context를 쉽게 읽을 수 있도록 도와주는 훅이에요.

복잡한 props 전달 없이도 데이터를 쏙쏙! 받아올 수 있죠.

📌 기본 구조

// Context 생성
const MyContext = createContext();

// 상위 컴포넌트에서 Provider로 감싸기
<MyContext.Provider value={공유할값}>
  <하위컴포넌트 />
</MyContext.Provider>

// 하위 컴포넌트에서 사용
const value = useContext(MyContext);

🧪 실습 예제

import React, { createContext, useContext } from 'react';

const ThemeContext = createContext('light');

function App() {
  return (
    <ThemeContext.Provider value="dark">
      <Toolbar />
    </ThemeContext.Provider>
  );
}

function Toolbar() {
  return (
    <div>
      <ThemedButton />
    </div>
  );
}

function ThemedButton() {
  const theme = useContext(ThemeContext);
  return <button>현재 테마: {theme}</button>;
}

💡 언제 쓰면 좋을까?

  • 테마 설정, 언어 설정 등 앱 전체에 영향을 주는 값이 있을 때
  • 로그인 정보처럼 여러 컴포넌트에서 접근이 필요한 상태가 있을 때

📋 요약 정리

기능 설명
전역 상태 공유 props 없이 하위 컴포넌트에서 상태 사용 가능
Context API createContext + useContext 조합으로 구성
성능 유의사항 변경 시 모든 하위 컴포넌트 재렌더링 가능성 있음

 

이제 useContext까지 익혔다면, 리액트의 주요 훅들은 거의 섭렵한 거예요! 💪

 

다음 단계는 지금까지 배운 내용을 정리하고, 각 훅을 실제 프로젝트에서 어떻게 조합해서 사용할 수 있는지 마무리하면서 이야기해볼게요.

지금까지 리액트의 주요 훅들useState, useEffect, useRef, useMemo, useCallback, useContext—을 하나씩 차근차근 살펴봤어요.

처음에는 각 훅이 언제, 왜, 어떻게 쓰는지 헷갈릴 수 있지만, 실제 프로젝트에서 직접 활용해보면 어느 순간 손에 익는 도구처럼 자연스럽게 쓰게 됩니다.

각 훅은 역할과 목적이 명확하기 때문에 상황에 맞춰 조합하는 연습을 많이 해보는 게 좋아요.

예를 들어 useContext로 전역 상태를 만들고, 그 안에서 useMemouseCallback으로 불필요한 렌더링을 줄이는 구조는 실전에서도 많이 사용돼요.

처음 훅을 공부할 땐 “이걸 왜 쓰지?” 싶은 느낌이 들 수 있지만, 리액트를 리액트답게 쓰는 핵심이 바로 이 훅들에 있답니다.

앞으로도 계속해서 다양한 상황에 훅을 적용해보면서 자신만의 개발 노하우를 만들어 보세요! 🚀

반응형
반응형

리액트 컴포넌트 설계 마스터:
Atomic Design, 합성, 상속 완전정복

리액트 컴포넌트를 더 유연하고 재사용 가능하게 만들고 싶다면,
Atomic Design과 합성, 상속
제대로 이해해야 해요! 🚀
반응형

 

안녕하세요, 개발자 여러분 😊
리액트를 처음 배울 땐 UI만 띄우는 것도 벅찼는데, 어느 순간부터는 코드의 재사용성구조의 일관성이 중요해지죠.
그래서 이번 글에서는 실무에서도 강력하게 통하는 Atomic Design 패턴부터, 컴포넌트 합성, 상속, 그리고 HOC, Render Props 패턴까지 한 번에 정리해드리려 합니다.

실전 예제와 함께 쉽게 설명해드릴 테니, 초보자 분들도 걱정 마세요!

 

1. Atomic Design 패턴이란? 🧬

처음 리액트를 배우다 보면, 어떤 컴포넌트를 어디까지 쪼개야 할지 고민되죠?

이럴 때 Atomic Design은 정말 유용한 설계 철학이자 구조화 전략이 되어줘요.

Atomic Design은 ‘작은 단위부터 큰 단위까지 계층적으로 UI를 구성하자’는 아이디어에서 출발했는데요, 이름처럼 ‘원자’ 단위로 UI를 분해하고 조립하는 접근입니다.

Atomic Design 구성요소 🔍

Atomic Design은 아래의 다섯 가지 구성요소로 나뉘어요:

  • Atoms: 버튼, 인풋 같은 더 이상 나눌 수 없는 UI 요소들
  • Molecules: Label + Input 같이 Atoms를 조합한 간단한 기능 단위
  • Organisms: Header, Footer처럼 여러 Molecule이 모인 복합 구조
  • Templates: 페이지의 레이아웃 구조 (Organisms 배치)
  • Pages: 실제 콘텐츠가 들어간 완성된 페이지

Atomic Design, 왜 쓰는 걸까? 🤔

Atomic Design의 가장 큰 장점은 재사용성과 일관성이에요.

프로젝트가 커질수록 컴포넌트가 복잡해지고 중복되는 코드가 생기기 쉬운데, 원자 단위로 분리해두면 언제든 조립해서 다른 곳에 사용할 수 있어요.

예를 들어,

어떤 버튼 하나를 수정했는데 그게 로그인 페이지, 회원가입 페이지, 마이페이지까지 다 바뀌어야 한다면? 🫠

이럴 땐 원자 단위로 관리하는 Atomic 구조가 유지보수의 신세계를 열어줍니다!

🧾 예제

// Atoms/Button.jsx
const Button = ({ children, onClick }) => (
  <button onClick={onClick} style={{ padding: '8px 16px', background: '#1b6ca8', color: '#fff' }}>
    {children}
  </button>
);

// Molecules/FormGroup.jsx
import Button from '../atoms/Button';
const FormGroup = () => (
  <div>
    <label>이메일</label>
    <input type="email" />
    <Button>제출</Button>
  </div>
);

 

이처럼 Atoms, Molecules, Organisms 순서로 쪼개서 설계하면 리팩토링도 쉽고, 다른 프로젝트에 복사해서 붙여넣기만 해도 호환이 척척 됩니다.

 

 

2. 컴포넌트 합성이란? 🔧

리액트에서 가장 강력한 개념 중 하나가 바로 컴포넌트 합성(Component Composition)이에요.

말 그대로, 컴포넌트를 다른 컴포넌트 안에 포함시켜서 더 큰 UI 구조를 만드는 거죠.

마치 레고처럼요!

합성은 코드 재사용성을 극대화하고, 공통 UI를 유연하게 확장할 수 있게 도와줘요.

이건 단순히 "다시 쓰기"의 개념이 아니라, 역할과 책임을 나눠서 컴포넌트를 더 똑똑하게 설계하는 방식이기도 하죠.

리액트의 기본 합성 방식: children 속성 🌱

리액트에서 컴포넌트를 합성하는 가장 기본적인 방법은 바로 props.children을 사용하는 거예요.

부모 컴포넌트가 자식 컴포넌트를 감싸고, 자식은 전달된 내용을 자신의 위치에 출력하죠.

// Wrapper.jsx
const Wrapper = ({ children }) => (
  <div style={{ border: '2px solid #1b6ca8', padding: '20px', borderRadius: '8px' }}>
    {children}
  </div>
);

// App.jsx
<Wrapper>
  <h2>안녕하세요!</h2>
  <p>이 부분은 Wrapper 컴포넌트 내부에 렌더링돼요.</p>
</Wrapper>

이렇게 하면 Wrapper 컴포넌트는 언제든지 다른 UI 요소를 감쌀 수 있는 공통 컴포넌트로 활용할 수 있죠.
정말 실용적인 방식이에요!

명시적 합성: 슬롯 패턴 🧩

때로는 children을 넘기는 것만으로는 부족할 수 있어요.

예를 들어 Header, Footer, Content를 각각 명확히 나누고 싶을 때는 슬롯 패턴을 쓰는 게 좋아요.

// Layout.jsx
const Layout = ({ header, content, footer }) => (
  <div>
    <header>{header}</header>
    <main>{content}</main>
    <footer>{footer}</footer>
  </div>
);

// App.jsx
<Layout
  header={<h1>타이틀입니다</h1>}
  content={<p>본문 내용입니다</p>}
  footer={<small>© 2025</small>}
/>
 

이 패턴을 사용하면 컴포넌트의 역할이 명확해지고, 구조도 훨씬 직관적이 돼요.

특히 복잡한 레이아웃에서 효과적입니다.

📋 합성 vs 상속

리액트 팀에서도 공식 문서에서 말하죠. "React는 상속보다 합성을 선호합니다."
이유는요? 합성은 컴포넌트 간 결합도를 낮추고, 더 명확한 데이터 흐름을 만들 수 있기 때문이에요.

 

그럼 다음 단계에서는 “왜 리액트는 상속보다 합성을 더 선호하는가?”를 실제로 비교해보며 알아볼게요!

 

 

3. 상속은 리액트에서 어떻게 다룰까? 🧩

리액트는 객체지향 프로그래밍에서 익숙한 상속을 기본 전략으로 삼지 않아요. 왜일까요? 🤔

컴포넌트 간의 관계가 복잡해지고 유지보수가 어려워지기 때문이에요.

리액트는 대신 합성(Composition)을 활용한 명확한 구성과 책임 분리를 더 선호하죠.

클래스 상속 vs 컴포넌트 합성 비교표 📊

구분 상속 (Inheritance) 합성 (Composition)
관계 구조 수직적 (부모-자식) 수평적 (독립적 조합)
유지보수 복잡하고 어렵다 간단하고 예측 가능
테스트 상위 클래스 영향 받음 각각 독립 테스트 가능
유연성 확장에 한계 있음 유연한 재사용 가능

왜 React는 상속보다 합성을 선택했을까? 💡

React의 설계 철학은 "컴포넌트는 독립적이고, 예측 가능하게"입니다.

상속 기반의 구조는 기능을 재사용하는 데는 편리할 수 있지만, 상위 컴포넌트의 변경이 하위 컴포넌트에 직접적인 영향을 준다는 치명적인 단점이 있어요.

반면, 합성은 이런 문제를 원천적으로 차단해 줍니다.

각 구성 요소는 독립적으로 존재하고, 합쳐질 때만 기능이 발생하니까요. 마치 퍼즐처럼요! 🧩

💡 실제 코드로 비교해보기

// 상속을 사용한 컴포넌트 설계 (지양)
class Button extends React.Component {
  render() {
    return <button style={{ background: 'blue' }}>{this.props.label};
  }
}

class DangerButton extends Button {
  render() {
    return <button style={{ background: 'red' }}>{this.props.label};
  }
}

// 합성을 사용한 컴포넌트 설계 (권장)
const Button = ({ children, style }) => (
  <button style={{ ...style, padding: '8px 16px' }}>{children}</button>
);

const DangerButton = ({ children }) => (
  <Button style={{ background: 'red', color: 'white' }}>{children}</Button>
);

 

보이시죠? 합성 방식은 훨씬 간단하고, 각 기능이 독립적이기 때문에 수정도 훨씬 쉬워요.

이게 바로 리액트가 합성을 선호하는 이유입니다.

 

 

4. HOC(Higher-Order Component) 패턴 🧠

리액트에서는 컴포넌트를 마치 함수처럼 다루는 패턴이 있는데요,

그게 바로 HOC(Higher-Order Component)입니다.

고차 컴포넌트라고도 불리죠.

이건 컴포넌트를 인수로 받아서, 새로운 기능을 덧붙인 새 컴포넌트를 반환하는 함수예요.

HOC의 기본 구조 🔁

고차 컴포넌트는 보통 이런 식으로 작성돼요:

// withLogger.js
const withLogger = (WrappedComponent) => {
  return (props) => {
    console.log("Props:", props);
    return <WrappedComponent {...props} />;
  };
};

// 사용 예시
const Hello = ({ name }) => <h1>안녕하세요, {name}님!</h1>;
const HelloWithLogger = withLogger(Hello);

<HelloWithLogger name="민지" />;

 

이렇게 HOC는 원래 컴포넌트를 변경하지 않고 확장할 수 있어서, 인증 처리, 로깅, 권한 제어 같은 부가 기능을 추가할 때 많이 사용돼요.

HOC 사용 시 주의할 점 ⚠️

  • Props 충돌 방지: HOC가 전달하는 props와 원본 컴포넌트의 props가 겹치지 않도록 조심해야 해요.
  • displayName 설정: 디버깅을 위해 HOC로 감싼 컴포넌트에 식별 가능한 이름을 설정하면 좋아요.
  • Wrapper 남용 금지: HOC를 너무 많이 중첩하면 컴포넌트 트리가 복잡해져서 디버깅이 어려워질 수 있어요.

🧾 HOC를 언제 쓰면 좋을까?

HOC는 반복적인 로직을 한 번에 추상화하고 싶을 때 유용해요.

예를 들어 로그인 여부 체크, API 통신 후 데이터 전달 등…

공통 기능을 재활용할 수 있는 곳이라면 HOC가 제격이죠.

하지만 너무 남발하면 컴포넌트의 흐름이 꼬이기 쉬우니, 적절한 선에서, 필요한 경우에만 사용하는 게 좋아요!

 

 

5. Render Props 패턴 이해하기 🎥

리액트에서 컴포넌트 재사용성을 높이는 또 다른 강력한 기법이 바로 Render Props 패턴입니다.

이건 말 그대로 props로 함수를 전달해서, 그 함수가 내부에서 렌더링할 내용을 결정하게 하는 방식이에요.

Render Props의 기본 구조 🔁

// MouseTracker.jsx
class MouseTracker extends React.Component {
  state = { x: 0, y: 0 };

  handleMouseMove = (event) => {
    this.setState({ x: event.clientX, y: event.clientY });
  };

  render() {
    return (
      <div style={{ height: '100vh' }} onMouseMove={this.handleMouseMove}>
        {this.props.render(this.state)}
      </div>
    );
  }
}

// App.jsx
<MouseTracker render={({ x, y }) => (
  <h1>마우스 위치: ({x}, {y})</h1>
)} />

 

위 예제를 보면, MouseTracker 컴포넌트는 마우스 좌표를 추적하지만 UI는 props로 받은 함수에 따라 다르게 바뀔 수 있어요.

로직과 표현을 분리해서 유연하게 설계할 수 있다는 게 핵심이죠.

Render Props vs HOC 비교 🤜🤛

구분 HOC Render Props
패턴 방식 함수로 컴포넌트를 감쌈 함수를 props로 전달
장점 코드 분리, 재사용 용이 로직과 UI 완벽 분리
단점 Wrapper 중첩 가능성 JSX 중첩, 가독성 저하

Render Props는 정말 강력하지만, this.props.render 같은 패턴이 익숙하지 않거나 JSX 트리 깊이가 커질 수 있다는 단점도 있어요.

그래서 React 훅이 등장한 이후로는 점점 쓰임이 줄어드는 추세이기도 하죠.

💡 언제 Render Props를 쓸까?

상태나 데이터를 추적하면서 렌더링 방식은 외부에서 결정되게 하고 싶을 때 Render Props가 정말 빛나요. 마우스 위치, 스크롤, resize 등 사용자 이벤트 처리에 딱이죠!

 

 

6. 합성과 상속의 실전 활용법 🔍

이제까지 컴포넌트 합성상속, 그리고 HOC, Render Props까지 배워봤죠.

이번에는 실무에서 어떻게 써먹는지 실전 사례와 함께 정리해볼게요. 🔧

📦 재사용 가능한 레이아웃 컴포넌트 만들기

대부분의 페이지는 공통된 구조를 갖고 있어요.

예를 들면 Header + Sidebar + Content 조합이죠.

이럴 땐 명시적 합성으로 구성 요소들을 slot처럼 받아보세요.

// Layout.jsx
const Layout = ({ header, sidebar, content }) => (
  <div className="layout">
    <header>{header}</header>
    <aside>{sidebar}</aside>
    <main>{content}</main>
  </div>
);

// 사용 예시
<Layout
  header={<Header />}
  sidebar={<Sidebar />}
  content={<PageContent />}
/>

🔐 인증 처리 공통 로직 추상화

로그인 여부에 따라 페이지 접근을 제어하는 기능, 여러 페이지에서 반복되죠?

이럴 땐 HOC 또는 Render Props로 공통 로직을 추상화하면 좋아요.

// withAuth.js (HOC 예시)
const withAuth = (Component) => {
  return (props) => {
    const isLoggedIn = useAuth();
    if (!isLoggedIn) return <LoginPage />;
    return <Component {...props} />;
  };
};

// 적용
const MyPage = () => <p>내 정보 페이지</p>;
const ProtectedMyPage = withAuth(MyPage);

🧩 합성과 상속을 혼용하면 안 되는가?

꼭 그런 건 아니에요. 아주 특수한 상황에서는 상속을 활용한 클래스 기반 확장이 유리할 수도 있어요.

예를 들어 Canvas나 WebGL 기반 렌더링 라이브러리와 통합할 때 말이죠.

다만, 리액트의 세계에선 합성이 거의 표준입니다.

✨ 마무리 팁

  • 공통 UI → 합성으로 모듈화 (children 또는 slot 방식)
  • 인증, 로깅, 로딩처리 → HOC나 Render Props로 추상화
  • UI 표현 분리 → Render Props 사용 (단점도 고려!)

실무에서는 이 패턴들을 적절히 조합해서 사용하는 것이 핵심이에요.

항상 "이 컴포넌트는 변경될 가능성이 있는가?", "재사용될 수 있는가?"를 고민하며 설계해보세요. 🙌 

여기까지 리액트의 컴포넌트 설계 패턴을 하나씩 살펴봤어요.

Atomic Design으로 UI를 체계적으로 쪼개고, 합성과 상속의 차이를 이해하면서, HOCRender Props로 기능을 추상화하는 방법까지!

사실 이 모든 걸 처음에 한 번에 다 이해하기는 쉽지 않아요.

너무 조급해하지 말고, 지금부터 하나씩 실습해보면서 감을 잡아보세요! 💪

앞으로 컴포넌트 구조를 설계할 때 오늘 배운 내용이 진짜 든든한 무기가 될 거예요.

 

다음 글에서는 리액트 훅(Hooks)을 활용해서 상태와 로직을 더 효율적으로 관리하는 방법에 대해 이야기해볼게요. 기대되시죠? 😊

반응형
반응형

리액트 컴포넌트의 다양한 구성 방법

컴포넌트 구성만 잘해도 리액트의 절반은 마스터한 거예요.
구조화된 코드로 유지보수성과 재사용성을 동시에 잡아보세요!
반응형

안녕하세요,  여러분! 😊

오늘은 리액트에서 가장 핵심이 되는 개념 중 하나인 "컴포넌트 구성 방식"에 대해 함께 알아보려 해요.

단순히 UI를 나누는 것을 넘어서, 유지보수성과 확장성을 고려한 설계 방식까지 꼼꼼히 다룰 예정입니다.

실무에서도 바로 적용할 수 있는 팁과 예제들을 준비했으니 끝까지 함께 해주세요 😊

 

1. 함수형 컴포넌트 기본 사용법 🧱

리액트를 처음 접하면 가장 먼저 마주하는 것이 바로 컴포넌트입니다.

특히 함수형 컴포넌트는 코드가 간결하고, React Hooks와 함께 사용하면 훨씬 강력한 기능을 구현할 수 있어요.

이 파트에서는 함수형 컴포넌트를 어떻게 정의하고 사용하는지, 그리고 어떤 구조로 작성하는 것이 좋은지를 알아봅니다.

함수형 컴포넌트란 무엇인가요?

함수형 컴포넌트는 단순히 JavaScript 함수로 정의된 컴포넌트입니다.

과거에는 클래스형 컴포넌트를 더 많이 사용했지만, 현재는 함수형 컴포넌트와 훅(Hooks)이 중심입니다.

 

가장 기본적인 함수형 컴포넌트 예시는 아래와 같습니다:

function Hello() {
  return <h1>안녕하세요!</h1>;
}

그리고 이것을 JSX 안에서 `<Hello />` 와 같이 호출하면 실제로 해당 컴포넌트가 렌더링됩니다.

Props로 데이터 전달하기

컴포넌트를 재사용 가능하게 만드는 핵심 요소가 바로 props입니다.

예를 들어 아래처럼 name을 전달해볼 수 있어요:

function Hello(props) {
  return <h1>안녕하세요, {props.name}!</h1>;
}

호출 시에는 이렇게 사용합니다: <Hello name="민수" />

정리 🧩

  • 함수형 컴포넌트는 간결하고 가독성이 좋다.
  • props를 이용해 유연한 데이터 전달이 가능하다.
  • 리액트 훅(Hooks)과 함께 쓰면 상태관리도 간단하게 가능하다.

이제 컴포넌트의 뼈대를 이해하셨다면, 다음으로 JSX 구조와 여러 컴포넌트 간의 관계를 좀 더 깊게 들여다보겠습니다.

 

 

2. JSX 구조와 다중 컴포넌트 구성 🏗️

JSX는 자바스크립트 안에서 HTML을 작성하는 것처럼 보이게 해주는 리액트의 문법입니다.

실제로는 자바스크립트의 문법적 확장(Syntax Extension)이며, React.createElement로 변환됩니다.

 

그런데 여러분, JSX 안에 컴포넌트를 여러 개 중첩해서 작성할 수 있다는 점 알고 계셨나요?

JSX로 컴포넌트 중첩하기

JSX에서는 divsection과 같은 태그로 여러 요소를 감싸거나, React.Fragment를 사용해 불필요한 DOM 생성 없이 컴포넌트를 중첩할 수 있어요.

function App() {
  return (
    <div>
      <Header />
      <MainContent />
      <Footer />
    </div>
  );
}

이런 식으로 여러 컴포넌트를 한 곳에 모아 하나의 UI를 구성할 수 있습니다.

그리고 각각의 컴포넌트는 다시 자신만의 자식 컴포넌트를 가질 수 있어요.

Fragment를 활용한 DOM 최소화

JSX는 반드시 하나의 부모 요소로 감싸야 하는 특징이 있습니다.

그런데 불필요한 div를 추가하고 싶지 않을 땐 어떻게 하냐구요?

 

바로 <React.Fragment> 또는 <> </> 문법을 사용할 수 있어요:

function Wrapper() {
  return (
    <>
      <h2>제목입니다</h2>
      <p>본문 내용입니다</p>
    </>
  );
}

불필요한 DOM 태그를 줄이면서도 JSX 규칙은 지킬 수 있어, 요즘은 정말 자주 쓰이는 방식입니다.

한눈에 정리 🧾

기법 설명
중첩 컴포넌트 상위 컴포넌트에서 여러 하위 컴포넌트를 조합하여 UI 구성
Fragment 사용 불필요한 DOM 없이 여러 요소를 하나의 컴포넌트에서 반환

 

이제 컴포넌트를 여러 개로 나눠서 어떻게 재구성할 수 있는지 감이 오시죠?

다음은 실제로 이 컴포넌트들이 어떻게 조합되는지, 그 구조를 더 깊이 있게 다뤄볼게요.

 

 

3. 컴포넌트의 조합과 부모-자식 구조 🧩

리액트에서 진짜 재미있는 부분은 바로 컴포넌트 조합이에요.

단순히 UI를 나눈 것뿐만 아니라, 데이터를 상위에서 하위로 어떻게 흐르게 할지도 설계의 핵심입니다.

부모 → 자식 데이터 전달: Props 흐름

컴포넌트 간 조합은 데이터를 props로 넘기면서 구성됩니다.

예를 들어 App 컴포넌트에서 Profile 컴포넌트에 데이터를 전달해볼게요.

// 부모 컴포넌트
function App() {
  return <Profile name="유나" age={28} />;
}

// 자식 컴포넌트
function Profile(props) {
  return (
    <div>
      <p>이름: {props.name}</p>
      <p>나이: {props.age}</p>
    </div>
  );
}

이처럼 컴포넌트는 마치 블록처럼 쌓아올릴 수 있고, 필요한 정보를 위에서 아래로 흘려보낼 수 있습니다.

자식 컴포넌트 안에 또 자식? 계층 구조 만들기

리액트에서는 컴포넌트를 계층적으로 구성할 수 있어요.

예를 들어, App 안에 Header가 있고, Header 안에 Logo와 Menu가 있는 구조처럼요.

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

이런 방식으로 코드를 구조화하면 협업이나 유지보수 시 훨씬 수월합니다.

👀 요약하자면...

  • 리액트 컴포넌트는 블록처럼 쌓고 조합하는 것이 가능하다.
  • 상위 컴포넌트는 하위 컴포넌트에게 props를 통해 정보를 전달한다.
  • 컴포넌트 계층을 명확히 구분하면 유지보수가 쉬워진다.

이제 컴포넌트가 서로 어떻게 연결되는지 감이 오셨죠?

다음 섹션에서는 개발자들이 즐겨 사용하는 패턴 중 하나인 '컨테이너-프리젠테이셔널 패턴'을 다뤄볼게요!

 

 

4. 컨테이너-프리젠테이셔널 패턴 🎭

컴포넌트를 구성할 때 가장 많이 사용되는 구조 중 하나가 바로 컨테이너(Container)프리젠테이셔널(Presentational) 패턴이에요.

이 방식은 로직과 UI를 명확히 분리할 수 있어서 협업과 유지보수에 탁월하답니다.

이 패턴이 왜 중요한가요?

리액트 앱이 커질수록 각 컴포넌트에 데이터 로직과 UI가 섞이기 쉬워요.

이러면 코드가 복잡해지고 재사용도 어려워지죠.

그래서 등장한 게 바로 이 패턴입니다.

 

쉽게 말해, '로직은 컨테이너가 처리하고, 화면은 프리젠테이셔널이 담당한다'는 거예요.

예제로 알아보는 분리 방법

// 프리젠테이셔널 컴포넌트
function UserProfile({ name, age }) {
  return (
    <div>
      <h2>{name}</h2>
      <p>{age}살</p>
    </div>
  );
}

// 컨테이너 컴포넌트
function UserProfileContainer() {
  const user = { name: "혜진", age: 25 };
  return <UserProfile name={user.name} age={user.age} />;
}

이런 방식으로 UI 로직은 단순하게, 데이터는 별도로 관리하면 확장성과 유지보수성이 극대화됩니다.

컨테이너 & 프리젠테이셔널 비교표 🔍

구분 컨테이너 프리젠테이셔널
역할 데이터 처리, 상태 관리 UI 렌더링 전담
관심사 비즈니스 로직 디자인 및 화면 구조

 

이제 프로젝트가 커져도, 로직은 로직대로, UI는 UI대로 명확하게 관리할 수 있겠죠?

다음은 프로젝트 구조를 깔끔하게 유지하는 폴더 구조 팁을 소개할게요!

 

 

5. 폴더 구조로 관리하는 방법 🗂️

리액트 프로젝트가 점점 커지면 한눈에 보기 어려운 '컴포넌트 지옥'에 빠질 수 있어요.

그래서 오늘은 폴더 구조를 체계적으로 구성해서 관리하는 법을 소개할게요!

기본적인 디렉터리 구조 예시

src/
│
├── components/          // 재사용 가능한 컴포넌트
│   └── Button.js
│
├── containers/          // 로직이 포함된 컴포넌트
│   └── UserContainer.js
│
├── pages/               // 페이지 단위 컴포넌트
│   └── Home.js
│
├── hooks/               // 커스텀 훅 저장소
│   └── useFetch.js
│
├── styles/              // 전역 또는 모듈 스타일
│   └── App.module.css
│
└── App.js               // 루트 컴포넌트

이렇게 구성하면 각 책임에 따라 폴더가 분리되므로 찾기도 쉽고 관리도 훨씬 깔끔해집니다.

상황별 폴더 분리 전략

  • 페이지 기반 프로젝트: pages 중심 구조 추천
  • 기능 기반 프로젝트: features 폴더 아래에 관련 모듈 묶기
  • 컴포넌트 수가 많을 경우: 공통 UI와 비즈니스 UI 구분

💡 팁 하나!

컴포넌트 파일이 많아지면 index.js를 활용해 모듈을 한 번에 export하는 방식도 매우 유용합니다.

이렇게 하면 import Button from '../components/Button' 대신

import { Button } from '../components'처럼 깔끔하게 사용할 수 있어요!

 

이제 폴더 구조까지 정리됐으니, 마지막으로 실무에서 바로 쓸 수 있는 컴포넌트 구성 팁들을 공유드릴게요 😉

 

 

6. 실무에서 유용한 컴포넌트 구성 팁 💡

이제 기본 개념과 구조는 충분히 익혔다면, 실무에서 바로 써먹을 수 있는 꿀팁들을 알아볼 시간이에요.

초보 개발자일수록 놓치기 쉬운 실수들을 방지하고, 더 효율적으로 개발하는 방법들을 알려드릴게요!

재사용 가능한 컴포넌트 만들기

비슷한 UI를 복붙하는 대신 props를 활용해 재사용 가능한 컴포넌트를 만들어보세요.

예를 들어 Button 컴포넌트 하나로 모든 버튼을 처리할 수 있습니다.

function Button({ label, onClick, type = "button" }) {
  return (
    <button type={type} onClick={onClick}>
      {label}
    </button>
  );
}

파일명과 컴포넌트명은 반드시 일치

이건 React의 암묵적 약속 같은 거예요.

컴포넌트 이름이 `ProfileCard`라면 파일명은 `ProfileCard.js`로 짓는 것이 좋아요. 찾기 쉽고, 협업 시에도 혼동을 줄일 수 있습니다.

상태관리는 필요한 컴포넌트에만

useStateuseEffect는 필요한 곳에만 쓰세요.

모든 컴포넌트에 상태를 두면 관리 지옥이 시작돼요.

최대한 상위 컴포넌트에서 처리하고, props로 전달하세요.

🚀 실무 팁 요약

  • 컴포넌트 이름과 파일명은 반드시 일치시킬 것
  • props를 통해 유연한 재사용 구조 만들기
  • 상태 관리는 꼭 필요한 경우에만, 상위에서 처리

이제 여러분도 실전에서 통하는 컴포넌트 설계를 할 수 있을 거예요! 🎉

 

 

마무리하며 🧩

지금까지 리액트 컴포넌트를 어떻게 구성하고 활용할 수 있는지에 대해 단계별로 살펴봤어요.

처음에는 컴포넌트 하나 만드는 것도 버거웠을 수 있지만, 이제는 구조화된 컴포넌트 설계재사용 가능한 패턴까지 이해하게 되셨을 거예요.

 

컴포넌트는 단순한 코드 블록이 아니라, 사용자와 상호작용하는 인터페이스의 조각입니다.

꾸준히 연습하고, 여러 프로젝트에서 실전 경험을 쌓다 보면 자연스럽게 '좋은 컴포넌트'가 무엇인지 눈에 들어오게 될 거예요.

 

다음 글에서는 이 컴포넌트들을 활용한 상태관리 기법컴포넌트 간 데이터 흐름에 대해 이야기해보려고 해요. 기대해주세요!

반응형
반응형

Tailwind CSS 최신버전 설치와 사용법 가이드 (Vite 프로젝트 기준)

Tailwind 설치가 복잡하다고요?
Vite와 함께라면 깔끔하고 빠르게 세팅할 수 있어요.
최신버전까지 완벽하게 반영된 설치 가이드를 지금 만나보세요!
반응형

 

안녕하세요, 여러분 😊

요즘 많은 분들이 Vite + Tailwind CSS 조합을 많이 사용하시더라고요.

빠른 번들링 속도, 모던한 설정, 게다가 유연한 확장성까지!

그런데 막상 시작하려고 하면 설치 방법이 헷갈릴 수 있어요.

그래서 오늘은 Vite로 프로젝트를 생성한 후 Tailwind CSS 최신버전을 적용하는 가장 깔끔한 방법을 정리해 드릴게요.

개발 초보자분들도 따라만 하면 OK!

실전에서 바로 써먹을 수 있는 사용법까지 친절하게 알려드립니다 💪

 

1. Vite 프로젝트 초기화 ⚙️

Tailwind CSS를 제대로 활용하려면 먼저 Vite 프로젝트부터 잘 세팅하는 게 중요해요.

요즘엔 CRA보다 Vite를 더 많이 쓰는 추세잖아요.

속도 차이가 어마어마하니까요. 그럼, 터미널부터 열고 같이 따라가 봅시다!

🚀 Vite 프로젝트 생성 명령어

우선 Node.js가 설치되어 있어야 해요. Node가 없다면 Node.js 공식사이트에서 설치 먼저 해주세요.

 

Node.js — 어디서든 JavaScript를 실행하세요

Node.js® is a JavaScript runtime built on Chrome's V8 JavaScript engine.

nodejs.org

 

npm create vite@latest tailwind-vite-app -- --template vanilla
  • tailwind-vite-app은 프로젝트 이름이에요. 원하는 이름으로 바꿔도 됩니다.
  • --template vanilla는 React 같은 프레임워크 없이 순수 HTML/JS 기반으로 구성된 템플릿이에요.

💡 명령 실행 후 폴더 이동 및 의존성 설치

cd tailwind-vite-app
npm install

여기까지 진행하면 Vite 프로젝트 뼈대가 완성됩니다.

구조는 심플해요.

index.html, main.js, 그리고 vite.config.js 파일이 핵심이죠.

📁 기본 디렉토리 구조

tailwind-vite-app/
├─ index.html
├─ main.js
├─ vite.config.js
├─ package.json
└─ node_modules/

이제 진짜 준비는 끝났어요.

다음 단계부터는 Tailwind CSS를 설치하고 설정하는 본격적인 작업이 들어갑니다!

 

 

2. Tailwind CSS 최신버전 설치 📦

자, 이제 본격적으로 Tailwind CSS를 설치할 차례입니다.

최신버전으로 설정하려면 몇 가지 패키지를 한 번에 설치해야 해요.

걱정 마세요. 명령어 하나면 끝입니다.

📦 설치 명령어

npm install -D tailwindcss postcss autoprefixer
  • tailwindcss: 핵심 프레임워크입니다.
  • postcss: Tailwind의 처리 과정을 도와주는 도구에요.
  • autoprefixer: 브라우저 호환성을 위한 자동 접두사 처리기입니다.

🛠️ 초기화 명령어로 설정 파일 생성

npx tailwindcss init -p

이 명령어를 실행하면 프로젝트 루트에 tailwind.config.jspostcss.config.js 파일이 자동으로 생겨요.

둘 다 Tailwind 작동에 꼭 필요한 설정 파일입니다.

📁 생성된 파일 구조

tailwind-vite-app/
├─ tailwind.config.js
└─ postcss.config.js

여기까지 문제 없이 따라오셨다면 거의 반은 끝난 거예요.

다음은 Tailwind가 적용될 수 있도록 실제 설정을 커스터마이징하는 단계로 넘어가 볼게요.

 

 

3. Tailwind 설정 파일 구성 ✏️

이제 우리가 만든 프로젝트에서 Tailwind CSS가 제대로 작동하도록 설정을 커스터마이징해볼게요. tailwind.config.js 파일 안에서 중요한 건 content 속성입니다.

이 속성은 Tailwind가 어떤 파일을 분석해서 클래스를 적용할지 알려주는 부분이에요.

📄 tailwind.config.js 파일 수정하기

// tailwind.config.js
export default {
  content: [
    "./index.html",
    "./src/**/*.{js,ts,jsx,tsx}"
  ],
  theme: {
    extend: {},
  },
  plugins: [],
}

이렇게 설정하면 index.html뿐만 아니라, src 폴더 내 모든 JS/TS/React 파일까지 Tailwind 클래스를 스캔합니다.

🧩 참고: JSX/TSX 사용 시 주의사항

React 기반 프로젝트를 사용할 경우 파일 확장자가 .jsx.tsx일 수 있으니 꼭 포함시켜야 해요.

안 그러면 Tailwind 클래스가 적용되지 않아 “왜 안 되지?” 하고 머리 싸매는 일이 생깁니다 😅

🗂 postcss.config.js는 그대로 두면 OK

postcss.config.js 파일은 Tailwind 설치 시 자동으로 아래와 같이 세팅돼요.

별도로 수정할 필요는 없습니다.

// postcss.config.js
export default {
  plugins: {
    tailwindcss: {},
    autoprefixer: {},
  },
}

이제 설정은 모두 끝났습니다!

다음 단계에선 실제로 Tailwind 클래스를 적용해 보고 브라우저에서 확인해 볼 거예요.

 

 

4. 글로벌 스타일 세팅 및 적용 🎨

Tailwind CSS가 프로젝트에 제대로 적용되려면 index.css 또는 style.css 같은 스타일 파일에서 Tailwind의 기본 디렉티브를 불러와야 해요.

이제 진짜 CSS와 만나는 순간이죠!

📄 style.css 파일 만들기

src 폴더 아래에 style.css 파일을 만들고, 다음과 같이 입력해 주세요.

@tailwind base;
@tailwind components;
@tailwind utilities;

이 세 가지 디렉티브는 Tailwind의 핵심이에요. 각각의 역할은 아래와 같아요 👇

디렉티브 설명
@tailwind base Reset과 Normalize 스타일이 포함된 기본 스타일 세트
@tailwind components 사용자 정의 가능한 컴포넌트 스타일 정의
@tailwind utilities Tailwind의 핵심 유틸리티 클래스들 포함

📌 main.js에서 스타일 불러오기

// src/main.js 또는 main.ts
import './style.css'

이 한 줄이 있어야 Tailwind CSS가 브라우저에 반영돼요.

의외로 이걸 빼먹어서 “왜 안되지?” 하는 분들 많더라고요 😅

 

이제 브라우저에서 Tailwind 클래스가 적용된 모습까지 확인할 수 있습니다!

다음 단계에선 진짜 Tailwind로 화면에 스타일을 입혀보는 실전 예제를 다뤄볼게요 💡

 

 

5. 실전 사용 예제 🧪

이제 준비는 끝났고, 진짜 재미있는 시간!

Tailwind CSS를 활용해서 예쁜 UI를 만들어봅시다.

이 섹션에서는 단순한 카드 컴포넌트를 예제로 사용해 Tailwind의 유틸리티 클래스가 어떻게 동작하는지 보여드릴게요.

💳 Tailwind로 만든 카드 UI 예제

<div class="max-w-sm mx-auto bg-white shadow-md rounded-lg overflow-hidden mt-10">
  <img class="w-full h-48 object-cover" src="https://source.unsplash.com/random/400x200" alt="랜덤 이미지">
  <div class="p-6">
    <h2 class="text-xl font-bold text-gray-800 mb-2">Tailwind Card</h2>
    <p class="text-gray-600">이 카드는 Tailwind CSS만으로 스타일링 되었습니다. 정말 깔끔하죠?</p>
    <button class="mt-4 bg-blue-500 text-white px-4 py-2 rounded hover:bg-blue-600 transition">자세히 보기</button>
  </div>
</div>

위 코드를 index.html<body> 안에 붙여넣고 실행해보세요.

놀라운 건 단 한 줄의 CSS 코드도 직접 작성하지 않았다는 것이죠!

📌 실전 적용 포인트 요약

  • flex, grid 같은 레이아웃 클래스도 직관적으로 사용 가능
  • 반응형 디자인은 sm, md, lg, xl 프리픽스로 처리 가능
  • 다크모드도 dark: 접두사 하나로 해결

Tailwind CSS는 그냥 “디자인 프레임워크”가 아니라, 생산성과 디자인 자유도를 동시에 만족시키는 도구예요.

여러분도 지금 바로 실습해보세요!

 

 

6. 자주 발생하는 문제 해결법 🧯

Tailwind를 적용하다 보면, 생각보다 자잘한 문제들이 종종 발생하죠.

“왜 클래스가 적용이 안 되지?” 같은 고민, 한 번쯤 해보셨을 거예요.

자주 발생하는 문제들과 그 해결책을 정리해봤어요.

❗ 클래스가 적용되지 않아요

  • style.css@tailwind 디렉티브가 빠졌는지 확인
  • main.js에서 style.css를 불러오지 않았는지 확인
  • tailwind.config.jscontent 경로가 누락되었는지 점검

❗ 빌드시 적용 안 되는 문제

Vite의 빌드 모드에서는 필요 없는 클래스가 자동으로 제거돼요.

근데 실제로는 동적으로 쓰는 클래스가 잘려나갈 수도 있거든요.

이럴 땐 Safelist 기능을 이용해서 강제로 포함시켜야 해요.

// tailwind.config.js
module.exports = {
  content: [...],
  safelist: ['bg-red-500', 'text-xl', 'md:grid'],
  ...
}

🔍 기타 팁

  • 브라우저 캐시를 지우고 새로고침 해보세요 (Ctrl+Shift+R)
  • VSCode를 쓰고 있다면 Tailwind IntelliSense 확장도 함께 쓰면 편리합니다!

처음엔 어렵게 느껴질 수도 있지만, 자꾸 해보다 보면 Tailwind는 어느 순간 "손에 붙는" 툴이 됩니다.

가볍게 실수하고 고치면서 익숙해지세요!

 

 

마무리 🎁

지금까지 Vite 프로젝트에서 Tailwind CSS 최신버전 설치와 사용법을 단계별로 알아봤습니다.

복잡해 보였던 Tailwind 설치 과정도 이렇게 정리해보니 생각보다 간단하죠?

Vite의 빠른 빌드 속도와 Tailwind의 유연한 스타일링이 만나면 정말 강력한 개발 환경이 완성됩니다.

이제 여러분도 Tailwind를 활용한 멋진 UI 컴포넌트를 만들 준비가 됐습니다.

이 글이 여러분의 프론트엔드 개발 여정에 조금이나마 도움이 되었다면 정말 기쁠 것 같아요!

다음 글에서는 Tailwind로 반응형 웹 만들기도 다뤄볼 예정이니 기대해주세요 📱💻

 

Tailwind는 결국 직관실전이 전부입니다.

직접 써보며 감을 잡고, 자주 활용해보세요! 그게 최고의 학습법입니다.

반응형
반응형

Django 모델 생성과 데이터 출력 완전 정복

데이터를 눈으로 확인할 수 있어야 진짜 실력이죠!
Django 모델부터 화면 출력까지 직접 해보는 실습, 지금 시작해볼까요?

 

 

안녕하세요, 여러분!

오늘은 Django 프로젝트에서 데이터 모델을 정의하고, 그 데이터를 실제 웹 페이지에서 출력하는 전 과정을 함께 살펴보려 해요.

예전엔 막연하게 느껴졌던 모델 정의와 마이그레이션, 그리고 admin에서 데이터 입력하고 템플릿에서 출력하는 흐름이 이제는 훨씬 친숙하게 느껴질 거예요.

이 글에서는 TodoItem 또는 BlogPost 같은 간단한 모델을 정의해서, admin 사이트에서 데이터를 넣고, 뷰 함수와 템플릿을 통해 화면에 출력하는 과정까지 단계별로 진행해볼 거예요.

 

1. 모델 정의하기 🧱

자, 이제 Django에서 가장 핵심이라고 할 수 있는 모델(Model)을 만들어볼 차례예요.

모델은 데이터베이스 테이블과 1:1로 매칭되는 클래스로, 우리가 다룰 데이터를 어떤 형태로 저장하고 사용할지를 정의해줍니다.

📝 모델 클래스 생성

예를 들어, 간단한 할 일(Todo) 목록을 만들고 싶다고 해봅시다.

이럴 땐 다음처럼 TodoItem 모델을 정의할 수 있어요:

from django.db import models

class TodoItem(models.Model):
    title = models.CharField(max_length=200)
    content = models.TextField()
    created_at = models.DateTimeField(auto_now_add=True)

    def __str__(self):
        return self.title

 

이 모델을 한 줄씩 살펴보면 다음과 같습니다:

필드 설명
title 할 일의 제목 (문자열, 최대 200자)
content 자세한 설명을 담을 수 있는 텍스트 필드
created_at 데이터가 생성된 시간 (자동 저장)

📁 모델 파일 저장 위치

모델 클래스는 보통 각 앱의 models.py 파일에 작성합니다.

예를 들어, 앱 이름이 todo라면 todo/models.py에 위 코드를 추가해 주세요.

  • models.py는 각 앱 안에 존재하며 데이터 구조를 정의하는 핵심 파일이에요.
  • __str__() 메서드는 admin이나 쉘에서 객체를 문자열로 표시할 때 유용합니다.

이렇게 간단한 모델 하나만 정의해도, Django는 이걸 기반으로 데이터베이스 테이블을 만들고 다양한 기능을 자동으로 제공해줘요. 정말 멋지지 않나요?

 

 

2. 마이그레이션 수행하기 🔄

모델을 만들었다고 해서 곧바로 데이터베이스에 테이블이 생기는 건 아니에요.

Django에서는 마이그레이션(migration)이라는 과정을 거쳐야 모델의 정의가 실제 데이터베이스 테이블로 반영됩니다.

🛠️ 마이그레이션 순서

  1. python manage.py makemigrations - 모델 변경 사항을 기반으로 마이그레이션 파일을 생성해요.
  2. python manage.py migrate - 실제로 데이터베이스에 테이블을 만들거나 수정해요.

직접 터미널에서 한 번 입력해볼까요?

# 마이그레이션 파일 생성
$ python manage.py makemigrations

# DB에 실제 반영
$ python manage.py migrate

 

명령어 실행 후 아래와 같이 출력되면 성공입니다 🎉

Migrations for 'todo':
  todo/migrations/0001_initial.py
    - Create model TodoItem

Operations to perform:
  Apply all migrations: admin, auth, contenttypes, sessions, todo
Running migrations:
  Applying todo.0001_initial... OK

🚧 잠깐! 이런 에러가 나올 수 있어요

  • App 'todo' could not be found → settings.py에 앱 등록 안 했을 가능성!
  • No changes detected → 모델을 수정한 적이 없을 수도 있어요.

마이그레이션은 한 번만 하고 끝나는 게 아니라, 모델에 변화가 생길 때마다 반복하게 될 작업입니다.

앞으로 자주 만나게 될 명령어니까 익숙해지는 게 좋아요!

 

 

3. Admin 사이트에 모델 등록하기 ⚙️

모델을 만들고 마이그레이션까지 완료했다면, 이제는 데이터를 직접 추가해볼 수 있어야겠죠?

Django는 기본적으로 Admin 사이트라는 강력한 기능을 제공합니다.

이걸 통해 직접 데이터를 추가하거나 수정할 수 있어요.

✅ 모델을 Admin에 등록하기

우선 admin.py 파일을 열어 아래처럼 코드를 추가해 주세요.

from django.contrib import admin
from .models import TodoItem

admin.site.register(TodoItem)

 

이렇게 하면 Admin 페이지에서 TodoItem 모델을 볼 수 있게 됩니다. 아주 간단하죠?

4. 슈퍼유저 생성 및 데이터 추가 👑

Admin에 로그인하려면 슈퍼유저(superuser) 계정을 먼저 만들어야 해요.

아래 명령어를 입력해 관리자 계정을 만들어 봅시다.

$ python manage.py createsuperuser

 

이제 서버를 실행하고 http://127.0.0.1:8000/admin에 접속해보세요.

  • 로그인하면 TodoItem이 목록에 보일 거예요.
  • 클릭해서 새 데이터도 직접 입력할 수 있답니다.

이제 모델을 만들고, Admin에 등록하고, 데이터를 입력하는 기본적인 백엔드 흐름을 완성했어요!

다음은 이 데이터를 뷰(View)템플릿(Template)을 통해 웹페이지에서 출력해보는 단계입니다.

 

 

5. 뷰 함수에서 QuerySet으로 데이터 조회 📦

Admin에서 입력한 데이터를 웹 페이지에 출력하려면 뷰(View) 함수에서 모델로부터 데이터를 조회해야 해요.

이때 사용하는 게 바로 Django의 ORM 기능 중 하나인 QuerySet입니다.

💡 QuerySet이란?

QuerySet은 데이터베이스에서 가져온 객체들의 집합이에요.

SQL을 직접 쓰지 않고 파이썬 문법으로 데이터를 조회할 수 있도록 도와줍니다.

예를 들어 TodoItem.objects.all()은 TodoItem 테이블의 모든 데이터를 불러오는 명령어랍니다.

🛠 뷰 함수에서 QuerySet 사용하기

자, 이제 views.py 파일에서 실제로 데이터를 가져오는 코드를 작성해볼게요.

from django.shortcuts import render
from .models import TodoItem

def todo_list(request):
    todos = TodoItem.objects.all()  # QuerySet: 전체 데이터 조회
    return render(request, 'todo/list.html', {'todos': todos})
  • TodoItem.objects.all()은 등록된 모든 할 일을 가져옵니다.
  • render() 함수는 데이터를 HTML 템플릿과 함께 반환해요.

🔗 URLconf에 뷰 연결하기

뷰 함수를 만들었으면 해당 함수에 접근할 수 있도록 urls.py에도 경로를 연결해줘야 합니다. 다음 코드를 todo/urls.py 또는 프로젝트의 urls.py에 추가해 주세요.

from django.urls import path
from . import views

urlpatterns = [
    path('todos/', views.todo_list, name='todo_list'),
]

이제 브라우저에서 http://127.0.0.1:8000/todos/ 주소로 접속하면, 뷰 함수가 동작하며 템플릿에 데이터를 전달하게 됩니다.

🧠 실전 활용 팁

  • .order_by('-created_at')으로 최신 항목부터 정렬할 수 있어요.
  • .filter()를 활용하면 특정 조건만 골라볼 수도 있어요.

지금까지 배운 내용은 Django의 데이터 흐름에서 가장 중요한 부분 중 하나입니다. 뷰 함수는 데이터와 템플릿 사이의 다리 역할을 하며, 이를 제대로 이해해야 Django를 진짜 잘 다룰 수 있게 돼요.

6. 템플릿에서 목록 출력 및 브라우저 확인 🖥️

이제 마지막 단계예요! 뷰에서 전달한 QuerySet 데이터를 템플릿 파일에 출력해보겠습니다. 이렇게 하면 우리가 만든 모델 데이터를 브라우저에서 직접 볼 수 있게 되죠.

📄 템플릿 파일 생성하기

우선 앱 폴더 안에 templates 폴더를 만들고, 그 안에 todo 폴더를 생성한 후 list.html이라는 파일을 만듭니다. 예시 경로는 다음과 같아요:

todo/
 ├── templates/
 │    └── todo/
 │         └── list.html

그리고 list.html에는 아래처럼 작성해 주세요.



  
  Todo 목록

할 일 목록
    {% for item in todos %}
        {{ item.title }}

        {{ item.content }}

        {{ item.created_at }}
      
    {% empty %}아직 등록된 항목이 없습니다.
    {% endfor %}
  


Django의 템플릿 언어인 {% for %}{{ }}를 활용해서 데이터를 반복 출력하고 있어요. 실제 데이터가 없을 경우엔 {% empty %} 블럭이 작동합니다.

📌 설정 점검 리스트

  • settings.pyTEMPLATES 설정에 'APP_DIRS': True가 되어 있는지 확인
  • 앱 이름이 INSTALLED_APPS에 포함되어 있어야 함

🌐 브라우저에서 확인해보기

이제 python manage.py runserver 명령으로 개발 서버를 실행하고, 브라우저에서 http://127.0.0.1:8000/todos/ 주소로 접속해보세요.

등록한 데이터가 목록으로 출력된다면... 성공입니다! 🎉

이 과정을 통해 데이터베이스 모델 생성 → Admin 등록 및 입력 → View에서 데이터 조회 → Template에서 출력까지 전체 웹 애플리케이션 흐름을 완성해본 셈이에요.

마무리하며 🧩

지금까지 우리는 Django에서 모델을 생성하고 데이터를 출력하는 전 과정을 순차적으로 경험해 봤습니다. 처음에는 복잡하게 느껴질 수 있지만, 한번 흐름을 타고 나면 의외로 논리적이고 직관적인 걸 느끼셨을 거예요.

전체 흐름을 요약하면 다음과 같습니다:

  1. 모델 정의 (models.py)
  2. 마이그레이션으로 DB에 반영
  3. Admin에 모델 등록 및 슈퍼유저 생성
  4. 뷰 함수에서 QuerySet으로 데이터 조회
  5. 템플릿에 데이터 넘기고 브라우저에 출력

이제 여러분은 Django 웹 개발에서 가장 기본이 되는 MVT(Model-View-Template) 패턴의 흐름을 완전히 이해한 셈입니다. 앞으로 더 복잡한 프로젝트를 다룰 때도 이 흐름은 그대로 유지되며, 이 토대를 기반으로 기능만 점점 확장되니까요.

다음 글에서는 모델 간 관계 설정(ForeignKey 등)과 리스트 정렬, 필터링 등의 고급 QuerySet 사용법까지 이어서 다뤄볼 예정이에요. 계속해서 함께 성장해봐요! 😊

반응형
반응형

Django 데이터베이스 연동과 ORM 이해

Django 프로젝트에서 데이터를 저장하고 꺼내 쓰는 게 어렵게 느껴지시나요?
ORM과 관리자 페이지의 마법을 경험해보세요!

 

 

안녕하세요, 여러분! 😊

웹 애플리케이션을 만들다 보면 사용자 데이터를 저장하거나, 게시글을 등록하고 수정하는 기능이 꼭 필요하죠.

이럴 때 가장 중요한 게 바로 데이터베이스 연동입니다.

이번 포스팅에서는 Django에서 가장 기본적으로 사용하는 SQLite 설정부터, 데이터를 자유자재로 다룰 수 있게 해주는 ORM(Object-Relational Mapping)의 개념과 사용법을 살펴볼 거예요.

또한 Django의 강력한 무기 중 하나인 Admin 페이지를 통해 데이터를 관리하는 방법까지 같이 다루겠습니다.

처음엔 조금 생소할 수 있지만, 하나씩 따라오다 보면 데이터베이스가 생각보다 친숙하게 느껴질 거예요. 😊

자, 그럼 본격적으로 시작해볼까요?

 

1. SQLite 설정과 마이그레이션 개념 🧱

Django는 기본적으로 SQLite라는 가볍고 사용하기 쉬운 데이터베이스를 기본으로 제공합니다.

설치할 것도 없고, 별도의 서버 없이 바로 사용 가능하다는 점에서 개발자들 특히 초보자에게 아주 좋은 선택이죠.

📂 settings.py에서 데이터베이스 확인하기

settings.py 파일을 열면 아래와 같이 DATABASES 설정이 이미 되어 있을 거예요.

DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.sqlite3',
        'NAME': BASE_DIR / 'db.sqlite3',
    }
}

 

이게 바로 SQLite를 사용하는 기본 설정입니다.

별다른 작업 없이 바로 데이터베이스 기능을 쓸 수 있게 도와주는 설정이죠.

🔧 마이그레이션이란 무엇인가요?

Django에서 모델을 만들었다고 해서 바로 DB에 반영되는 건 아니에요.

모델의 변경사항을 데이터베이스에 적용하려면 마이그레이션(Migration) 작업을 해야 합니다.

이 과정을 쉽게 설명하면 이래요:

  • 모델을 만들면 그걸 반영할 SQL 코드가 자동으로 생성됨 → makemigrations
  • 생성된 SQL 코드를 실제 DB에 반영 → migrate

터미널에서 아래 명령어를 실행해보세요.

python manage.py makemigrations
python manage.py migrate

이제 모델에 정의한 구조대로 테이블이 데이터베이스에 생성됩니다. 짠! 너무 어렵지 않죠?

📋 요약 테이블

개념 설명
SQLite Django 기본 데이터베이스로, 가볍고 별도 설치가 필요 없음
makemigrations 모델 변경 사항을 기반으로 마이그레이션 파일 생성
migrate 생성된 마이그레이션을 실제 데이터베이스에 적용

 

2. Django 모델 정의 방법과 필드 종류 📝

웹 서비스에서 정보를 저장하고 관리하려면 모델(Model)을 정의해야 합니다.

Django에서는 이 모델이 곧 데이터베이스 테이블과 1:1로 매칭돼요.

각 모델은 Python 클래스 형태로 정의되며, 클래스의 속성 하나하나가 데이터베이스의 컬럼이 됩니다.

📌 모델 클래스 예시

from django.db import models

class Post(models.Model):
    title = models.CharField(max_length=100)
    content = models.TextField()
    created_at = models.DateTimeField(auto_now_add=True)

위 예시는 Post라는 게시글 모델입니다.

title, content, created_at이라는 세 개의 필드를 가지고 있고, 각각 문자열, 긴 텍스트, 생성 시간 정보를 저장해요.

🔍 주요 필드(Field) 종류 알아보기

필드 이름 설명
CharField 짧은 문자열 저장. max_length 필수 지정
TextField 긴 텍스트 저장 (제한 없음)
IntegerField 정수 값 저장
DateTimeField 날짜/시간 저장. auto_now_add 또는 auto_now로 자동 설정 가능
BooleanField True/False 값 저장

💡 유용한 모델 설정 옵션들

  • verbose_name: 필드의 이름을 관리자 페이지에서 보기 쉽게 설정
  • default: 기본값 지정 가능
  • null, blank: 비워도 되는지 여부 설정

모델은 단순히 테이블을 정의하는 걸 넘어서, Django 전반의 데이터 흐름을 담당해요.

앞으로의 기능 구현에서도 계속 활용되니, 지금 확실하게 익혀두는 걸 추천드립니다!

 

 

3. ORM과 QuerySet으로 데이터 다루기 ⚙️

Django에서는 ORM(Object-Relational Mapping) 기능을 통해 Python 코드만으로도 데이터베이스를 자유롭게 다룰 수 있습니다.

즉, 복잡한 SQL을 몰라도 객체 지향적으로 데이터를 삽입하고 수정하며 삭제할 수 있어요.

이게 진짜... 너무 편해요.

🛠️ 기본적인 데이터 조작 예시

# 객체 생성
post = Post(title="첫 글", content="장고 너무 재밌다!")
post.save()

# 모든 데이터 조회
Post.objects.all()

# 조건에 맞는 데이터 조회
Post.objects.filter(title__contains="첫")

# 하나만 가져오기
Post.objects.get(id=1)

# 데이터 수정
post = Post.objects.get(id=1)
post.title = "수정된 제목"
post.save()

# 데이터 삭제
post.delete()

보시다시피 .save(), .filter(), .get(), .delete() 같은 메서드만 알면 데이터를 마음대로 다룰 수 있어요.

쿼리셋(QuerySet)을 이용하면 복잡한 조건도 체이닝해서 쉽게 만들 수 있고요.

🔍 자주 쓰는 QuerySet 메서드 정리

메서드 설명
all() 모든 객체를 조회
filter() 조건에 맞는 객체만 조회
get() 특정 조건에 맞는 하나의 객체 조회 (없거나 둘 이상이면 오류)
exclude() 특정 조건을 제외한 객체 조회
order_by() 정렬 기준 지정

이런 메서드들을 조합하면, 꽤 복잡한 데이터 필터링도 몇 줄 안 되는 코드로 깔끔하게 해결할 수 있어요.

그러니까요, 진짜 ORM은 신세계예요.

⚠️ 주의할 점!

  • get()은 여러 개의 결과가 있거나 없을 때 예외 발생! 항상 try/except로 감싸는 게 좋아요.
  • save()를 안 하면 DB에 반영되지 않아요. 반드시 저장해 주세요!

Django ORM은 입문자에게도 매우 직관적으로 다가오는 도구입니다.

복잡한 SQL 없이도 실무 수준의 데이터 처리가 가능하다는 건 정말 큰 장점이에요.

 

 

4. Django Admin 사이트 활용법 🛠️

Django를 처음 접하면 누구나 놀라는 기능이 있어요.

바로 Admin 사이트입니다!

개발자가 만든 모델을 관리자 페이지에서 바로 추가, 수정, 삭제할 수 있는 인터페이스가 기본 제공된다는 건... 솔직히 말해 사기급이에요.

🔐 관리자 계정 만들기

먼저 관리자 페이지를 사용하려면 슈퍼유저(superuser)를 생성해야 합니다.

터미널에서 아래 명령어를 입력해보세요.

python manage.py createsuperuser

이후 사용자명, 이메일, 비밀번호를 입력하면 관리자 계정이 생성됩니다.

서버를 실행하고 http://localhost:8000/admin 으로 접속해보세요!

🧩 모델 등록하기

관리자 페이지에서 직접 데이터를 관리하려면 모델을 admin에 등록해야 합니다.

해당 앱의 admin.py 파일을 열고 아래처럼 작성해보세요.

from django.contrib import admin
from .models import Post

admin.site.register(Post)

이제 관리자 페이지에 "Post" 메뉴가 생겨요.

여기서 데이터를 추가하거나 삭제할 수 있는 폼 UI가 자동으로 생성됩니다.

🎨 관리자 화면 커스터마이징

관리자 페이지의 기본 UI도 커스터마이징이 가능합니다.

예를 들어 제목, 생성일만 리스트에 보이게 하려면 아래처럼 코드를 수정하면 돼요.

class PostAdmin(admin.ModelAdmin):
    list_display = ('title', 'created_at')

admin.site.register(Post, PostAdmin)

이런 식으로 list_filter, search_fields, readonly_fields 등 다양한 옵션을 지정해 관리 페이지를 내가 원하는 대로 꾸밀 수 있어요.

🧾 관리자 페이지 요약

기능 설명
createsuperuser 관리자 계정 생성
register() 모델을 admin 사이트에 등록
list_display 목록에서 보여줄 필드 지정

이처럼 Django의 Admin 사이트는 개발자가 데이터 관리 페이지를 손쉽게 만들 수 있도록 도와줍니다.

특히 초기에 빠르게 MVP를 만들거나 내부 관리 도구가 필요할 때 아주 유용해요.

 

 

5. 예고: 모델 간 관계 설정 개념 🔗

지금까지는 하나의 모델만을 기준으로 데이터를 다뤘어요.

하지만 현실 세계의 데이터는 대부분 서로 관계를 맺고 있죠.

예를 들어, 사용자(User)가 여러 개의 게시글(Post)을 작성하거나, 하나의 카테고리(Category)가 여러 개의 상품(Product)을 포함하는 구조 말이에요.

🔄 다양한 관계(Relation)의 형태

  • 1:1 관계 (OneToOneField) – 예: 사용자와 프로필
  • 1:N 관계 (ForeignKey) – 예: 사용자와 게시글
  • N:N 관계 (ManyToManyField) – 예: 사용자와 그룹

이런 관계를 정의하려면 Django 모델에서 ForeignKey, OneToOneField, ManyToManyField 같은 필드를 사용합니다.

 

다음 블로그 글에서는 이렇게 모델 간 관계를 정의하고 이를 활용하는 방법을 심도 있게 다룰 예정이에요.

예를 들어 게시글(Post)에 작성자(User)를 연결하거나, 하나의 글에 여러 개의 댓글(Comment)을 연결하는 구조를 만드는 실전 예제를 보여드릴게요.

 

복잡한 데이터 구조도 Django에서는 간단하고 우아하게 정의할 수 있다는 사실, 꼭 다음 편에서 확인해보세요!

 

 

6. 마무리 정리 및 다음 글 안내 📌

이번 포스팅에서는 Django에서 데이터를 다루기 위해 꼭 알아야 할 데이터베이스 설정, 모델 정의, ORM 사용법, 그리고 Admin 사이트 활용까지 폭넓게 살펴봤습니다.

처음에는 생소할 수 있는 개념들이지만, 직접 따라 해보면서 실습해보면 어느 순간 "아, 이제 좀 알겠다!" 하는 순간이 분명 찾아올 거예요.

데이터를 설계하고 관리하는 과정은 앞으로 더 다양한 기능들을 구현할 때 아주 중요한 기반이 됩니다.

그리고 다음 글에서는 드디어 모델 간 관계 설정에 대해 본격적으로 들어갑니다.

ForeignKey를 이용해 사용자와 게시글을 연결하고, 댓글을 추가해보는 등 조금 더 현실적인 예제를 다룰 예정이에요.

 

🔥 이제 여러분은 Django 데이터 모델링의 첫 단계를 마스터했습니다.

      다음 단계에서는 훨씬 더 유기적이고 확장성 있는 데이터 구조를 함께 만들어봅시다!

반응형
반응형

첫 Django 앱 생성과 간단한 페이지 구현

"Hello, Django!" 한 줄로 시작되는 웹 개발의 첫 걸음,
지금부터 함께 만들어 볼까요?

 

 

안녕하세요, 개발자 여러분!

오늘은 Django 프로젝트를 시작하고, 여러분만의 첫 웹 페이지를 만들어보는 시간을 가져보려 해요.

Django 프레임워크를 설치하고 프로젝트를 구성해본 분이라면, 이제 본격적으로 실습에 들어가볼 때죠.

페이지를 하나 만들고 웹 브라우저에서 “Hello, Django!”라는 메시지를 확인할 수 있는 과정을 따라가면서, 뷰(View), 템플릿(Template), URLconf의 연계 방식도 자연스럽게 익혀볼 수 있어요.

처음엔 복잡하게 느껴질 수 있지만, 한 걸음씩 차근차근 따라가면 어렵지 않답니다.

특히 이번 글에서는 실습 위주로 접근할 예정이라 초보자분들도 큰 부담 없이 이해하실 수 있을 거예요.

 

1. 새로운 앱 생성과 프로젝트에 등록하기 🧱

이제 본격적으로 Django 프로젝트 안에 첫 번째 앱을 만들어볼 시간입니다.

Django에서는 웹 애플리케이션의 기능 단위를 "앱(app)"이라는 구조로 나누어 구성합니다.

예를 들어, 게시판 기능, 회원 관리 기능, 블로그 기능 등이 각각 독립된 앱으로 만들어질 수 있어요.

📌 앱 생성 명령어

python manage.py startapp pages

 

이렇게 입력하면 pages라는 이름의 디렉토리가 생성되고, 그 안에 Django 앱의 기본 구조가 자동으로 세팅됩니다.

🛠 프로젝트에 앱 등록하기

앱을 생성했으면 Django 프로젝트에 해당 앱을 등록해줘야 해요.

settings.py 파일을 열고, INSTALLED_APPS 리스트에 아래와 같이 추가해줍니다.

INSTALLED_APPS = [
    ...
    'pages',
]

 

이렇게 하면 pages 앱이 프로젝트에 포함되어 Django가 이 앱을 인식하게 됩니다.

앞으로 이 앱 안에서 뷰, URL, 템플릿 등을 구성해나갈 거예요.

🧾 요약 정리

  • python manage.py startapp pages 명령어로 앱 생성
  • settings.pyINSTALLED_APPS에 'pages' 추가

 

이제 다음 단계로 넘어가서, 우리가 만든 pages 앱 안에 첫 번째 뷰 함수를 작성해보겠습니다.

 

 

2. 간단한 뷰 함수 작성하기 🧠

이제 우리가 만든 pages 앱 안에서 실제로 페이지를 보여주는 뷰 함수(view function)를 작성해보겠습니다. 이 뷰 함수는 사용자의 요청을 받아 HTML 페이지를 반환하는 역할을 하죠.

🔧 views.py 파일 수정

우선 pages/views.py 파일을 열고 아래와 같이 간단한 함수를 만들어주세요.

from django.shortcuts import render

def home(request):
    return render(request, 'home.html')

 

이 함수는 request 객체를 받아 home.html이라는 템플릿 파일을 렌더링(render)해서 클라이언트에게 반환합니다.

아직 템플릿 파일은 없지만, 이건 다음 단계에서 만들어볼 거예요!

💡 render 함수란?

Django에서 render() 함수는 템플릿 시스템과 뷰를 연결하는 역할을 합니다.

내부적으로 Template 객체를 로드하고, 전달받은 데이터를 해당 템플릿에 렌더링한 뒤 HTML을 반환하죠.

아주 자주 쓰이기 때문에 꼭 익혀두세요!

🧾 요약 정리

  • views.py에서 home() 뷰 함수 생성
  • render(request, 'home.html')로 템플릿 반환

다음 단계에서는 home.html 템플릿 파일을 실제로 만들어볼 거예요.

우리의 "Hello, Django!" 메시지를 웹에 띄워볼 준비 되셨죠?

 

 

3. 템플릿 디렉터리 및 HTML 파일 생성 ✨

이제 우리의 뷰 함수가 불러올 home.html 파일을 만들어야 할 차례입니다.

Django에서는 HTML 파일을 templates 폴더에 보관하는 게 일반적이에요.

📁 템플릿 디렉터리 구조 만들기

앱 디렉터리(pages/) 안에 templates/pages 폴더를 생성하세요.

이 구조는 Django가 템플릿을 앱과 연결된 이름으로 찾을 수 있도록 도와줍니다.

pages/
├── templates/
│   └── pages/
│       └── home.html

 

이제 home.html 파일을 만들어 아래와 같은 내용을 넣어보세요.

<!-- pages/templates/pages/home.html -->
<!DOCTYPE html>
<html lang="ko">
<head>
  <meta charset="UTF-8">
  <title>My First Django Page</title>
</head>
<body>
  <h1>Hello, Django!</h1>
</body>
</html>

 

이 HTML 파일은 우리가 웹 브라우저에서 확인하게 될 페이지입니다.

정말 간단하죠? 하지만 여기서 중요한 건 템플릿 파일이 뷰 함수에서 지정한 경로와 정확히 일치해야 한다는 점이에요.

💡 템플릿 폴더 명명 규칙 팁

  • 앱 이름과 동일한 하위 폴더를 templates 내부에 생성해야 충돌 없이 사용할 수 있어요.
  • ex) pages 앱이면 templates/pages/home.html 경로로 생성

이제 이 템플릿을 웹 페이지로 띄울 수 있도록 URLconf 설정을 해보러 갈까요?

 

 

4. URLconf 설정으로 뷰 연결하기 🔗

뷰와 템플릿을 만들었지만, 아직 웹 브라우저에서 페이지를 열 수 없어요.

왜냐하면 URLconf를 설정하지 않았기 때문이죠.

Django에서는 urls.py를 통해 요청 URL과 뷰를 연결해줍니다.

📌 앱 수준 URLconf 만들기

pages 앱 디렉터리 안에 urls.py 파일을 새로 만들어 아래와 같이 작성해 주세요.

# pages/urls.py
from django.urls import path
from . import views

urlpatterns = [
    path('', views.home, name='home'),
]

 

이제 앱 수준의 URLconf는 준비가 끝났고, 이를 프로젝트 루트 urls.py에 연결만 하면 됩니다.

🔧 프로젝트 루트 urls.py 연결

프로젝트 디렉터리에 있는 config/urls.py 파일을 열어 아래와 같이 include를 이용해 pages 앱을 연결합니다.

from django.contrib import admin
from django.urls import path, include

urlpatterns = [
    path('admin/', admin.site.urls),
    path('', include('pages.urls')),
]

 

이제 http://localhost:8000/ 경로로 접속하면 pages.views.home 뷰가 실행되고, home.html이 브라우저에 출력됩니다.

🧾 요약 정리

  • pages/urls.py에 뷰 함수 연결
  • config/urls.py에서 include('pages.urls')로 연결 완료

URL 설정까지 끝났다면, 다음 단계는 드디어! 웹 브라우저에서 페이지를 확인해보는 일입니다.

화면에 'Hello, Django!'가 뜨는 그 짜릿한 순간을 함께 느껴볼까요?

 

 

5. 웹 브라우저에서 결과 확인하기 🌐

자, 모든 준비는 끝났습니다!

이제 로컬 개발 서버를 실행해서 우리가 만든 페이지가 잘 나오는지 확인해보는 일만 남았어요.

이 순간이 바로 Django 개발의 가장 첫 성취감이 느껴지는 타이밍이죠 😊

▶ 서버 실행

python manage.py runserver

 

명령어를 실행하면 다음과 같이 서버가 8000번 포트에서 실행되었다는 메시지를 볼 수 있습니다:

Watching for file changes with StatReloader
Performing system checks...

System check identified no issues (0 silenced).
April 17, 2025 - 22:05:00
Django version 4.x.x, using settings 'config.settings'
Starting development server at http://127.0.0.1:8000/
Quit the server with CONTROL-C.

 

이제 브라우저를 열고 주소창에 http://127.0.0.1:8000 또는 http://localhost:8000 를 입력해 보세요!

🎉 확인 결과

화면에 커다란 "Hello, Django!" 문구가 보였다면... 성공입니다! 🎉

지금 여러분은 Django 기반 웹페이지를 세상에 (아직은 로컬이지만) 처음 띄운 셈이에요.

💡 오류가 발생했을 때는?

  • 템플릿 경로가 잘못되었을 가능성 확인
  • URLconf 연결 상태 점검

이제 HTML이 정상 출력되는 걸 확인했으니,

다음 단계에서는 템플릿을 조금 더 효율적으로 관리하는 방법—바로 템플릿 상속 기법을 소개해드릴게요.

 

 

6. 베이스 템플릿 작성과 템플릿 상속 🏗️

Django 템플릿 시스템은 반복되는 HTML 코드 구조를 효율적으로 관리할 수 있도록 템플릿 상속 기능을 제공합니다. 이번 단계에서는 베이스 템플릿을 만들어서 여러 페이지에서 공통으로 사용할 수 있게 구성해볼 거예요.

📁 베이스 템플릿 만들기

pages/templates/ 디렉토리 하위에 base.html이라는 파일을 만들고 다음과 같이 작성합니다.

<!-- pages/templates/base.html -->
<!DOCTYPE html>
<html lang="ko">
<head>
  <meta charset="UTF-8">
  <title>{% block title %}Django App{% endblock %}</title>
</head>
<body>
  <header>
    <h1>🎈 Django App</h1>
  </header>
  
  <main>
    {% block content %}{% endblock %}
  </main>
  
  <footer>
    <p>&copy; 2025 My Django</p>
  </footer>
</body>
</html>

 

이제 home.html 파일을 다음과 같이 수정해서 base.html을 상속받도록 할게요.

<!-- pages/templates/pages/home.html -->
{% extends 'base.html' %}

{% block title %}홈{% endblock %}

{% block content %}
  <h2>Hello, Django!</h2>
  <p>이 페이지는 템플릿 상속을 사용하여 만들어졌습니다.</p>
{% endblock %}

 

이제 home.htmlbase.html의 구조를 그대로 따라가면서 본문 내용만 바꿀 수 있어요.

앞으로 다른 페이지도 이 구조를 재사용하면 유지보수가 훨씬 편해지겠죠?

💡 템플릿 상속의 장점

  • 여러 페이지에서 반복되는 코드(HTML 구조)를 한 곳에서 관리 가능
  • 유지보수 시 공통 구조 수정이 한 파일로 해결
  • 템플릿 블록으로 내용만 손쉽게 커스터마이징

이제 하나의 틀만 만들어두면 여러 페이지를 손쉽게 만들 수 있어요.

뭔가 웹사이트 같아지지 않았나요? 😄

마무리 ✨

오늘은 Django에서 첫 번째 앱 생성부터 웹 페이지 출력까지의 흐름을 따라가봤습니다.

하나하나 따라가면서 뷰, 템플릿, URL 설정의 연결 구조를 눈으로 확인하고, 직접 HTML로 결과를 출력해보셨을 거예요.

단순한 "Hello, Django!"지만, 처음으로 화면에 결과를 띄웠다는 경험은 아주 중요한 첫걸음입니다.

더 나아가 base.html을 통해 템플릿 상속도 경험해보면서, 앞으로 프로젝트가 커졌을 때 어떻게 효율적으로 유지보수를 할 수 있을지에 대한 감도 잡으셨을 거라 믿어요.

 

다음 시간에는 이 구조를 기반으로 동적인 데이터를 다루는 방법, 폼 처리, 모델 연동 등 더 본격적인 웹 애플리케이션 기능 구현으로 넘어가볼게요.

지금처럼 한 걸음씩 차근차근 따라오신다면 Django가 훨씬 더 친숙하게 느껴질 거예요 😊

📌 오늘 배운 핵심 요약

  • startapp 명령어로 앱 생성 및 프로젝트에 등록
  • 뷰 함수 render()로 템플릿 반환
  • 템플릿 폴더 및 HTML 작성
  • URLconf를 통한 뷰 연결
  • 템플릿 상속으로 공통 레이아웃 구성

 

처음 웹 서비스를 만들어보는 분들께 정말 꼭 필요한 흐름이었죠?

다음 포스트에서는 이 앱에 사용자 입력을 처리할 수 있는 폼(form) 기능을 추가해볼 예정입니다.

기대해 주세요!

반응형
반응형

Django 앱 구조와 URL·뷰·템플릿 완전 정복

“Django에서 프로젝트랑 앱은 도대체 뭐가 다르죠?”
처음 Django를 배우는 분들이 가장 많이 묻는 질문 중 하나예요.
사실 이 부분만 확실히 잡아도 개발이 훨씬 쉬워져요.

 

 

안녕하세요, 여러분!

오늘은 Django 웹 개발에서 가장 중요한 구조적인 개념들을 하나하나 짚어보는 시간을 가져보려고 해요.

특히 초보자 분들이 헷갈려하기 쉬운 "프로젝트 vs 앱", URL과 뷰 연결 방식, 템플릿 활용법, 정적 파일 처리 방법까지!

MVT 아키텍처의 흐름을 시각적으로, 예제를 통해 명확하게 설명드릴게요.

이 포스팅을 마치고 나면 여러분은 Django의 기본 골격을 스스로 이해하고 구성할 수 있을 거예요.

 

1. 프로젝트 vs 앱: Django의 구조 이해하기 🏗️

Django를 시작하면 가장 먼저 접하게 되는 용어가 바로 프로젝트(project)앱(app)이에요.

이 두 개념을 명확히 구분하는 것이 굉장히 중요해요.

왜냐하면 우리가 만드는 서비스가 커질수록 구조를 제대로 이해하지 않으면 코드가 뒤엉켜버리기 때문이죠.

📌 프로젝트(Project)란?

프로젝트는 Django에서 웹 애플리케이션 전체를 포함하는 최상위 공간이에요.

예를 들어, 여러분이 ‘블로그 사이트’를 만든다고 하면, 그 전체 웹사이트가 하나의 프로젝트가 되는 거예요.

settings.py, urls.py, wsgi.py, asgi.py 같은 핵심 설정 파일들이 이 프로젝트 안에 포함되어 있죠.

📦 앱(App)이란?

앱은 프로젝트 내부에 포함되어 하나의 독립적인 기능 단위를 담당하는 컴포넌트입니다.

예를 들어

게시판 기능은 하나의 앱으로, 사용자 인증 기능은 또 다른 앱으로 구성할 수 있어요.

이처럼 앱은 재사용성과 모듈화를 위해 만들어지는 거죠.

📁 기본 폴더 구조 예시

myproject/
├── manage.py
├── myproject/
│   ├── __init__.py
│   ├── settings.py
│   ├── urls.py
│   ├── asgi.py
│   └── wsgi.py
└── blog/         ← 앱
    ├── admin.py
    ├── apps.py
    ├── models.py
    ├── views.py
    ├── urls.py
    └── templates/

✅ 비교 정리

구분 프로젝트(Project) 앱(App)
의미 전체 웹사이트의 틀 기능별 모듈
역할 설정 및 라우팅 총괄 뷰, 모델, 템플릿 포함
재사용성 낮음 높음

🎯 요약 포인트

  • 프로젝트는 설정의 중심, 앱은 기능의 중심!
  • 하나의 프로젝트에 여러 개의 앱을 넣는 구조가 Django의 핵심.

 

 

2. MVT 아키텍처 요청-응답 흐름 🔁

Django는 MVT 아키텍처라는 구조를 기반으로 동작합니다.

여기서 MVT는 Model - View - Template의 약자예요.

많은 사람들이 헷갈리는 부분이 바로 'View'인데요.

다른 프레임워크에서의 View와는 조금 다른 개념이랍니다.

🔄 MVT의 구성요소

  • Model
  • : 데이터베이스와 연동되는 계층으로, 데이터 구조와 저장 로직을 담당해요.
  • View
  • : URL 요청을 받아 처리하고, 어떤 데이터를 보여줄지 결정하는 비즈니스 로직 처리 담당이에요.
  • Template
  • : 사용자에게 보여지는 HTML 화면을 담당하는 프론트엔드 계층입니다.

📡 요청-응답 흐름

MVT 구조는 사용자의 요청이 서버에 도달했을 때 어떤 방식으로 응답이 이루어지는지를 순서대로 처리해요.

아래는 그 흐름을 간단히 도식화한 거예요.

1. 사용자가 웹 브라우저에서 특정 URL 요청
2. Django가 URLconf에서 해당 URL 패턴 탐색
3. 매칭되는 View 함수 호출
4. View 함수가 Model을 통해 데이터 처리
5. View가 Template에 데이터 전달
6. Template이 HTML 생성하여 사용자에게 응답 반환

🌐 예시 흐름: 블로그 글 목록 보기

사용자가 /posts/ 주소로 접속하면 Django는 urls.py에서 이 URL과 매칭되는 View 함수를 찾습니다.

그 View는 Post 모델에서 데이터를 조회하고, 이를 템플릿(post_list.html)에 전달하여 HTML 페이지를 렌더링한 후 응답으로 전송해요.

🔁 한 문장 요약

URL → View → Model → Template → 사용자 응답 이 흐름을 이해하는 것이 Django 개발의 핵심이에요!

 

 

3. URLconf와 View 함수 연결 방법 🔗

Django에서 사용자가 어떤 페이지를 요청하면,

그 요청은 URLconf(URL configuration)라는 설정 파일을 통해 해당 View 함수와 연결됩니다.

이 과정을 이해하면, 웹사이트의 구조를 자유자재로 설계할 수 있어요!

📁 urls.py의 역할

urls.py는 URL 패턴을 정의하는 파일이에요.

이 파일을 통해 "이 URL 요청이 들어오면, 어떤 View 함수로 전달할지"를 결정하죠.

마치 웹사이트의 길 안내를 해주는 네비게이션 같은 존재예요.

📨 View 함수는 무슨 역할을 하나요?

View 함수는 요청을 받아 필요한 데이터를 조회하거나 가공한 후, 적절한 응답(HTML, JSON 등)을 만들어 반환합니다.

비즈니스 로직의 중심이며, 종종 모델과 템플릿 사이의 가교 역할도 해요.

🔧 간단한 연결 예제

# app/views.py
from django.http import HttpResponse

def home(request):
    return HttpResponse("Hello, Django!")

# app/urls.py
from django.urls import path
from . import views

urlpatterns = [
    path('', views.home, name='home'),
]

위 코드에서 사용자가 / 경로로 접속하면 home 함수가 실행되고, 그 결과로 "Hello, Django!"가 출력되는 구조예요.

🔁 프로젝트 전역 URLconf 연동

앱 단위로 작성된 urls.py는 프로젝트의 메인 urls.py에서 include()를 통해 불러와야 작동합니다.

예를 들어:

# project/urls.py
from django.contrib import admin
from django.urls import path, include

urlpatterns = [
    path('admin/', admin.site.urls),
    path('', include('app.urls')),  # 앱의 URLconf 포함
]

📚 URL, View 연결 요약

  • View는 함수를 통해 사용자 요청을 처리하고 응답을 만들어냅니다.
  • urls.py는 "이 요청은 이 View로!" 라는 연결고리 역할을 합니다.
  • 프로젝트와 앱의 urls.py를 함께 구성해야 Django가 전체 요청을 인식해요.

 

 

4. 템플릿 엔진 사용 방법과 정적 파일(Static files) 처리 개념 🧩

Django의 매력 중 하나는 강력한 템플릿 엔진과 정적 파일 시스템이에요.

이 두 가지를 잘 활용하면, 백엔드 개발자가 프론트엔드 영역까지 손쉽게 다룰 수 있어요.

HTML 코드에 Python 데이터도 넣을 수 있고, CSS, JS, 이미지도 깔끔하게 관리할 수 있죠.

🧠 템플릿 엔진이란?

Django의 템플릿 엔진은 HTML 문서 안에 Python 데이터와 로직을 삽입할 수 있도록 도와주는 도구입니다.

변수 출력, 조건문, 반복문 등을 통해 동적인 HTML 페이지를 생성할 수 있어요.

📝 템플릿 사용 예시

<h1>Hello, {{ username }}!</h1>
{% if is_admin %}
<p>관리자 권한이 있습니다.</p>
{% else %}
<p>일반 사용자입니다.</p>
{% endif %}

이처럼 {{ 변수 }}를 통해 데이터를 출력하고, {% if %} ... {% endif %} 문법으로 조건을 제어할 수 있어요.

📁 정적 파일 처리(Static files)

정적 파일(static files)은 HTML과 함께 로드되는 CSS, JS, 이미지 파일을 말해요.

Django는 이러한 파일들을 별도로 관리하기 위한 시스템을 제공해요.

📦 폴더 구조 예시

myproject/
├── static/
│   ├── css/
│   │   └── style.css
│   └── js/
│       └── script.js
├── templates/
│   └── index.html

🔗 템플릿에서 정적 파일 사용하기

{% load static %}

{% load static %}을 선언하고, {% static '경로' %}를 사용하면 정적 파일을 안전하게 불러올 수 있어요.

이렇게 하면 개발 환경과 운영 환경 모두에서 호환성이 좋아집니다.

🧷 정리 한 줄 요약

템플릿은 Django가 HTML에 데이터를 주입할 수 있게 해주는 똑똑한 도구이고, 정적 파일 처리는 스타일과 동작을 책임지는 프론트엔드 리소스를 관리하는 방법이에요!

 

 

5. 템플릿 상속 개념 소개 🧱

여러 페이지를 가진 웹사이트를 만들다 보면, 모든 페이지에 공통적으로 들어가는 부분이 있죠?

예를 들면, 상단 메뉴, 푸터, 로고 같은 것들요.

이런 부분을 매번 복사해서 붙여넣는 건 비효율적이고, 나중에 유지보수도 어렵습니다.

Django는 이런 문제를 템플릿 상속이라는 기능으로 아주 깔끔하게 해결해줘요.

🏛️ 베이스 템플릿이란?

base.html 또는 layout.html이라고 불리는 이 베이스 템플릿은 공통 레이아웃을 정의해두는 템플릿이에요.

각 개별 페이지는 이 템플릿을 {% extends %}로 확장하고, {% block %} 태그를 통해 특정 부분만 바꿔서 사용할 수 있어요.

📐 베이스 템플릿 예제


  {% block title %}My Site{% endblock %}
  

My Website Header
  
    {% block content %}{% endblock %}
  © 2025 MySite
  


위 템플릿은 타이틀과 본문 부분을 block으로 정의해뒀고, 다른 템플릿에서 이걸 상속받아 사용하게 돼요.

🔁 상속받는 하위 템플릿 예시 --------------------------------------------------------------------------


{% extends "base.html" %}

{% block title %}홈 페이지{% endblock %}

{% block content %}

<p data-ke-size="size16">환영합니다! 여기는 홈입니다.</p>

{% endblock %}

home.htmlbase.html을 확장하면서 제목과 콘텐츠 영역만 바꿔 끼워넣은 거예요.

이렇게 하면 전체 구조는 유지하면서 내용만 바꾸는 게 가능해요!

💡 템플릿 상속의 장점

  • 중복 코드 제거로 생산성 향상 💼
  • 레이아웃 일관성 유지로 유지보수 용이 🔧
  • 각 페이지 별 핵심 콘텐츠에만 집중할 수 있음 🎯

📌 요약 정리

템플릿 상속은 기본 골격은 하나로 통일하고, 내용만 바꾸는 웹 페이지 관리 방식이에요.

규모가 커질수록 이 방식의 장점은 더 강력해집니다!

 

 

6. 예제와 함께 전체 흐름 복습하기 📚

지금까지 배운 내용을 하나의 흐름으로 묶어서 복습해볼까요?

Django에서 하나의 웹 페이지가 동작하는 과정을 예제 기반으로 정리해보면, 머릿속이 훨씬 깔끔해질 거예요!

📝 목표: 사용자 이름을 입력받아 인사하는 페이지 만들기

이 예제에서는 사용자가 /hello/홍길동처럼 이름을 URL로 전달하면, 그 이름을 화면에 보여주는 간단한 인사 페이지를 만들어요.

① URLconf 설정

# app/urls.py
from django.urls import path
from . import views

urlpatterns = [
    path('hello//', views.hello_user, name='hello'),
]

② View 함수 작성

# app/views.py
from django.shortcuts import render

def hello_user(request, username):
    context = {'username': username}
    return render(request, 'hello.html', context)

③ 템플릿 작성 

{% extends "base.html" %}

{% block title %}인사하기{% endblock %}

{% block content %}

{{ username }}님, 반가워요! 👋

{% endblock %}

💡 전체 동작 흐름 요약

  1. 1. 사용자가 /hello/홍길동을 입력
  2. 2. urls.py에서 해당 URL을 찾아 hello_user 뷰로 연결
  3. 3. 뷰 함수에서 받은 이름을 템플릿에 전달
  4. 4. hello.htmlbase.html을 상속받아 출력
  5. 5. 사용자 화면에 환영 메시지 출력 완료!

🎯 마무리 요약

Django는 URL, View, Template의 구조를 이해하고 적절히 연결하는 것만으로도 강력한 웹 페이지를 쉽게 만들 수 있어요.

오늘 배운 흐름을 반복해 익혀두면 어떤 웹 페이지든 자신 있게 구현할 수 있답니다!

 

 

마무리 🏁

여기까지 따라오시느라 정말 고생 많으셨어요!

오늘은 Django의 웹 개발에서 가장 핵심이 되는 구조들, 즉 프로젝트와 앱의 개념, MVT 아키텍처 흐름, URL과 View 연결, 템플릿과 정적 파일 처리, 템플릿 상속까지 모두 다뤄봤습니다.

이 글에서 다룬 내용은 Django 개발의 기반이 되는 핵심이기 때문에 꼭 숙지하시는 걸 추천드려요.

처음엔 복잡해 보일 수 있지만, 한두 번 실습해보면 금방 손에 익는 구조랍니다.

 

앞으로 게시판, 블로그, 쇼핑몰, 포트폴리오 사이트 등을 만들 때도 오늘 배운 흐름을 그대로 적용할 수 있어요. 혹시 다음 단계로 어디서부터 손대야 할지 막막하신가요? 그렇다면 지금 만든 구조에 간단한 게시글 모델과 폼을 추가해보세요. 점점 더 강력한 기능을 얹어갈 수 있을 거예요.

 

다음 글에서는 Django 모델(Model)과 데이터베이스 ORM에 대해 다뤄볼 예정이니 기대해주세요!

“코드는 직접 짜보는 만큼 이해도는 깊어진다.”

반응형
반응형

개발환경 구축과 Django 프로젝트 시작

처음 Django를 설치하고 프로젝트를 시작할 때,
어디서부터 손대야 할지 막막하셨죠?
이 글 하나면 OK! 🔧
반응형

 

안녕하세요,  여러분! 😄

오늘은 여러분이 Django라는 강력한 웹 프레임워크를 사용해 첫 프로젝트를 시작할 수 있도록 개발환경을 세팅하고, Django 프로젝트를 만드는 첫걸음을 함께 해보려 해요.

이 글은 단순히 코드를 나열하는 튜토리얼이 아니라, 왜 그렇게 해야 하는지까지도 친절히 설명할 테니, 걱정은 접어두시고 커피 한잔과 함께 따라와 주세요! ☕

특히 Python 기초만 알고 있어도 충분히 이해할 수 있게 구성되어 있어, Django 입문에 완벽한 가이드가 될 거예요.

 

1. 가상환경이란? 왜 필요한가요?

처음 Django를 공부하거나 개발 프로젝트를 시작할 때 가장 먼저 듣게 되는 단어가 바로 가상환경(virtual environment)입니다. 근데, 왜 굳이 이런 걸 써야 할까요? 🤔

그냥 파이썬 깔고 바로 쓰면 되는 거 아닌가요?

사실, 간단한 스크립트나 학습용 코드에는 크게 상관이 없을 수 있어요.

하지만 프로젝트마다 사용하는 라이브러리 버전이 다르거나 충돌이 발생하는 상황에서는 이야기가 달라집니다.

예를 들어, 프로젝트 A에서는 Django 3.2를, 프로젝트 B에서는 Django 4.0을 쓴다고 해보죠.

이런 경우, 시스템 전체에 Django를 설치했다면 서로 충돌이 날 수밖에 없어요.

💡 가상환경을 쓰면 좋은 점!

  • 프로젝트마다 독립된 환경 제공 → 충돌 방지
  • 불필요한 시스템 오염 방지 → 유지보수 용이
  • 협업 시, 환경 일관성 유지 → 배포도 쉬움

📌 실제로 어떻게 동작하나요?

가상환경은 단순히 파이썬 인터프리터와 라이브러리 디렉토리를 복사해서 독립된 환경으로 만들어 주는 거예요.

우리가 만든 가상환경 안에서는 시스템에 설치된 다른 라이브러리와 상관없이, 내가 원하는 버전만 설치해서 쓸 수 있죠.

🧪 예시로 알아보는 상황

항목 가상환경 없음 가상환경 사용
Django 버전 시스템 전역에 하나만 존재 프로젝트마다 다르게 가능
라이브러리 충돌 다른 프로젝트 영향 가능 서로 독립적
배포 환경 패키지 충돌로 실패할 수 있음 확실한 환경 재현 가능

가상환경에 대해 좀더 알아보실 분들은 아래 포스트를 참고해주세요

https://firstcoding.net/74

 

파이썬 가상환경(venv, virtualenv)으로 프로젝트 환경을 깔끔하게 관리하는 법

파이썬 가상환경(venv, virtualenv)으로 프로젝트 환경을 깔끔하게 관리하는 법프로젝트마다 패키지 버전이 달라서 자꾸 충돌난다면? 파이썬 가상환경으로 환경관리를 시작해보세요! 🧪  안녕하

firstcoding.net

 

 

이제 감이 오셨죠? 😀

다음 글에서는 가상환경을 직접 만들고 Django를 설치하는 실습을 해볼게요!

명령어 한 줄 한 줄, 왜 그런지까지 설명하며 알려드릴게요.

바로 다음 단계에서 만나죠!

 

 

2. 가상환경 만들기와 Django 설치하기

이번에는 실제로 가상환경을 만들고, Django를 설치하는 과정을 하나씩 해보겠습니다.

윈도우 사용자 기준으로 설명하지만, macOS나 리눅스도 거의 동일해요.

다만 경로나 명령어가 조금 다를 수 있으니 참고만 해주세요.

⚙️ Step-by-step: 가상환경 만들기

  1. 프로젝트를 위한 디렉토리를 만듭니다. 
  2. CLI 환경에서 아래 코드 실행
  3. mkdir my_django_project cd my_django_project
  4. 가상환경을 생성합니다. venv는 파이썬 기본 제공 가상환경 툴입니다.
    python -m venv venv
  5. 가상환경을 활성화합니다.
    • Windows: venv\Scripts\activate
    • macOS/Linux: source venv/bin/activate

🎉 명령어를 실행한 후, 프롬프트 앞에 (venv)가 붙으면 성공적으로 가상환경에 들어온 것입니다!



🌐 Django 설치하기

이제 본격적으로 Django를 설치해볼게요.

pip는 Python의 패키지 관리자로, Django도 pip를 통해 간단히 설치할 수 있어요.

pip install django

 


설치가 완료되면 다음과 같이 버전을 확인할 수 있습니다.

django-admin --version

💡 Tip: requirements.txt 만들기

앞으로 협업하거나 서버에 배포할 땐 requirements.txt 파일이 꼭 필요해요.

지금 설치된 패키지를 저장해두면 나중에 pip install -r requirements.txt 명령으로 똑같이 설치할 수 있죠!

pip freeze > requirements.txt

 

📎 설치 요약 정리

단계 명령어
1. 디렉토리 생성 mkdir my_django_project
2. 가상환경 생성 python -m venv venv
3. 가상환경 활성화 venv\Scripts\activate
4. Django 설치 pip install django

이제 준비는 끝났습니다. 🎉

다음 단계에서는 Django 프로젝트를 생성하고 실행하는 법을 배워볼 거예요.

준비되셨죠? 바로 다음 STEP에서 만나봐요!

 

 

3. Django 프로젝트 생성과 폴더 구조 이해

이제 드디어 본격적인 Django 프로젝트를 시작할 차례입니다!

가상환경도 만들고 Django도 설치했으니, 첫 프로젝트를 만들어볼까요?

이 단계에서는 django-admin이라는 명령어를 사용해서 새로운 프로젝트를 생성합니다.

🚀 django-admin startproject 명령어 사용하기

django-admin startproject config .

 

위 명령어에서 config는 프로젝트 이름입니다.

원하는 이름으로 바꿔도 돼요! 뒤에 붙은 .(점)은 “현재 디렉토리에 바로 생성하겠다”는 뜻인데, 폴더 구조가 한 번 더 중첩되는 걸 막아줘요.

💡 점(.)을 안 붙이면 어떻게 되나요?

config/config/처럼 디렉토리가 두 겹이 돼서 조금 헷갈릴 수 있어요.

.을 붙여주면 루트 디렉토리에 필요한 파일만 깔끔하게 생성됩니다!

📂 Django 프로젝트 기본 구조 설명

명령어를 실행하면 다음과 같은 디렉토리 구조가 만들어집니다.

my_django_project/
├── manage.py
├── config/
│   ├── __init__.py
│   ├── settings.py
│   ├── urls.py
│   ├── asgi.py
│   └── wsgi.py
파일/디렉토리 설명
manage.py Django 명령어를 실행할 때 사용하는 메인 스크립트 (예: runserver, migrate)
settings.py 프로젝트의 모든 설정 정보 (DB, 앱 등록, 경로 등)
urls.py URL 라우팅을 담당하는 곳, 어떤 URL로 어떤 기능을 실행할지 지정
wsgi.py / asgi.py 웹 서버와 연결하기 위한 인터페이스 스크립트 (배포 시 사용)

VSCode 실행



아직은 생소한 파일이 많을 수 있지만, 앞으로 차근차근 다룰 거니까 걱정 마세요. 😊

이제 다음 단계에서는 manage.py를 이용해 Django 개발 서버를 실행하고, 환영 페이지를 띄워보는 실습을 진행할게요!


4. 개발 서버 실행하고 환영 페이지 보기

지금까지 프로젝트를 생성했고, 폴더 구조도 확인했죠?

이제 실제로 Django가 어떻게 동작하는지, 개발 서버를 실행해보고 웹 브라우저에서 결과를 확인해볼 시간이에요.

딱 10초만에 Django의 기본 환영 페이지를 만나볼 수 있어요! 🌈

▶️ 개발 서버 실행하기

가상환경이 활성화되어 있고, Django 프로젝트 디렉토리 안에 있다면 아래 명령어만 입력하면 됩니다.

python manage.py runserver

 

✅ 명령어 실행 후, 아래와 같은 메시지가 보이면 성공적으로 서버가 실행된 거예요!

Watching for file changes with StatReloader
Performing system checks...

System check identified no issues (0 silenced).
April 17, 2025 - 22:30:02
Django version 4.x, using settings 'config.settings'
Starting development server at http://127.0.0.1:8000/
Quit the server with CONTROL-C.

🌐 Django 환영 페이지 보기

웹 브라우저를 열고 주소창에 다음을 입력해보세요:

http://127.0.0.1:8000/

 

 

이 주소는 로컬호스트(localhost), 즉 여러분의 컴퓨터를 의미합니다.

성공적으로 실행되었다면, Django의 멋진 초록색 체크 표시가 있는 환영 화면을 보게 될 거예요! 🎉

🤔 에러가 난다면?

  • 가상환경이 활성화되지 않았을 수 있어요.
  • venv\Scripts\activate 또는 source venv/bin/activate를 다시 확인!
  • manage.py 파일이 있는 디렉토리에서 실행했는지 확인!

📌 Django 개발 서버의 특징

항목 설명
자동 감지 코드 수정 시 서버 재시작 없이 자동 반영
기본 포트 8000번 포트를 사용 (--port 옵션으로 변경 가능)
개발 전용 운영 환경에서는 사용 ❌ (보안상 취약함)

어떠셨나요?

처음으로 브라우저에서 Django 프로젝트가 돌아가는 걸 확인하면 꽤 뿌듯하실 거예요!

다음은 프로젝트 내부 파일들 — 특히 settings.py, urls.py — 들의 역할을 자세히 살펴보겠습니다. 🛠️

 

 

5. 프로젝트 주요 파일 설명 (settings.py, urls.py 등)

Django 프로젝트를 만들고 서버도 돌려봤는데, 폴더 안에 있는 파일들... 솔직히 처음 보면 좀 겁나죠? 😅

settings.py, urls.py, wsgi.py, asgi.py 등등… 이름은 많은데 도대체 다 무슨 역할을 하는 걸까요?

이번에는 이 중요한 파일들을 하나하나 살펴보면서, Django가 어떤 구조로 움직이는지 확실히 이해해보는 시간을 가질게요. 🧠

⚙️ settings.py – 프로젝트 설정의 본부

Django에서 가장 중요한 파일 중 하나예요.

이름 그대로 프로젝트 전반의 설정을 담당합니다.

데이터베이스 연결부터 앱 등록, 보안 설정까지 모두 여기에 있어요.

  • INSTALLED_APPS – 사용할 앱 목록 (예: admin, auth 등)
  • MIDDLEWARE – 요청/응답을 가로채는 중간 처리기 리스트
  • DATABASES – 어떤 DB를 쓸지 (기본은 SQLite)
  • STATIC_URL – 정적 파일(CSS, JS 등)의 기본 경로

🌐 urls.py – URL과 View를 연결해주는 라우터

클라이언트가 어떤 URL로 접근했을 때 어떤 페이지나 기능을 보여줄지를 설정하는 파일입니다.

즉, URL ↔ View 함수의 연결고리 역할을 하죠.

from django.contrib import admin
from django.urls import path

urlpatterns = [
    path('admin/', admin.site.urls),
]

 

위 코드는 /admin/ 경로로 접속하면 Django의 관리자 페이지로 이동하도록 설정한 예입니다.
앞으로 여기에 직접 만든 뷰 함수들을 추가해가며 프로젝트를 확장할 거예요!

🌐 wsgi.py & asgi.py – 서버와 Django의 연결고리

이 두 파일은 웹 서버(Gunicorn, Daphne 등)가 Django 앱과 통신할 수 있도록 해주는 인터페이스 스크립트입니다. 단, 개발 단계에서는 거의 사용할 일이 없어요. 프로젝트를 실제 배포할 때 사용된다고 기억만 해두시면 충분합니다!

파일 설명
wsgi.py 동기 방식 배포용 (예: Gunicorn)
asgi.py 비동기 웹 서버용 (예: Daphne, Uvicorn)


이처럼 Django 프로젝트의 각 파일은 서로 역할을 나누어 구조적으로 움직입니다.

각각을 이해하고 있으면 앞으로 앱 개발 시 어떤 파일을 수정해야 하는지, 어디를 먼저 살펴봐야 하는지 훨씬 수월해질 거예요.

 

 

6. 자주 묻는 질문 및 초보자 팁 ✨

처음 Django를 접할 때 헷갈리는 부분이 정말 많죠?

특히 에러 메시지가 뜨면 ‘뭐가 뭔지 모르겠어!’ 하고 당황하기 쉬워요.

그래서 이 섹션에서는 실제 수강생이나 독자들이 자주 물어봤던 질문과, 제가 직접 겪으며 깨달았던 꿀팁들을 정리해드릴게요.

“아 이거 나만 몰랐던 거 아니었구나!” 싶은 순간이 올지도 몰라요. 😅

❓ 자주 묻는 질문 (FAQ)

  1. Q. 가상환경은 프로젝트마다 꼭 만들어야 하나요?
    네, 무조건 만드는 걸 추천드려요!
    그래야 라이브러리 버전 충돌을 막을 수 있고, 협업 시에도 동일한 환경을 유지할 수 있어요.
  2. Q. pip install이 안 될 때는 어떻게 해야 하나요?
    인터넷 연결 상태를 먼저 확인해보세요.
    그리고 python -m pip install --upgrade pip로 pip 버전을 최신으로 유지하는 것도 중요해요.
  3. Q. runserver 했는데 접속이 안 돼요!
    가상환경이 활성화되어 있는지, manage.py가 있는 디렉토리에서 명령어를 실행했는지 다시 확인해보세요.
  4. Q. Django 버전은 어떤 게 좋아요?
    최신 버전을 사용하는 것이 보안과 성능 면에서 좋아요.
    다만, 공식 문서에서 권장하는 LTS 버전을 사용하는 것도 안정적인 선택이에요.

💡 실수 방지용 초보자 꿀팁!

  • 매번 서버를 끄고 켤 필요 없어요!
    Django는 코드 변경 사항을 자동으로 감지해서 반영해줘요.
    서버만 켜두면 실시간으로 확인할 수 있어요.
  • settings.py는 손댈 땐 복사본부터!
    혹시 오류 나면 되돌릴 수 있도록 settings.py 파일을 백업해두세요.
  • 에러 메시지를 잘 읽자!
    Django는 에러 메시지를 정말 친절하게 써줘요.
    천천히 읽으면 해결 실마리가 보입니다.
  • pyenv나 poetry 등 도구도 나중에 배워보자!
    파이썬 프로젝트를 더 잘 관리할 수 있는 도구들이에요.
    초보 땐 몰라도 되지만, 나중엔 꼭 도움이 될 거예요.

처음부터 완벽할 필요는 없어요. 저도 Django 처음 시작할 땐 runserver 한 줄 치는 것도 긴장했거든요. 😅

중요한 건 에러를 두려워하지 않고 천천히 쌓아가는 태도입니다.

이제 여러분도 Django의 첫 문을 열었으니, 계속해서 앱을 만들고 실습하면서 자신감을 키워보세요!

 

 

Django 입문의 첫 발걸음을 축하합니다! 🎉

여기까지 오신 여러분, 정말 수고 많으셨어요. 👏

처음에는 생소했던 가상환경 생성부터 Django 설치, 프로젝트 구조 이해, 그리고 서버 실행까지 모두 직접 해보셨습니다.

처음 접하면 낯설고 어색했던 단계들이 이제는 조금은 익숙해졌을 거예요.

Django는 강력하고 유연한 웹 프레임워크입니다.

아직은 단순한 “Hello Django” 단계지만, 앞으로 여러분은 이걸 기반으로 블로그, 게시판, 로그인 시스템, API 서버까지 만들 수 있어요.

이제 정말 웹 개발자가 되는 길이 시작된 것이죠.

 

다음 단계로는 앱 생성 및 라우팅, 데이터베이스 모델 만들기, CRUD 구현, 템플릿 시스템 등으로 나아가면 됩니다.

이 블로그 시리즈에서도 그 모든 과정들을 차근차근 함께 걸어갈 예정이니, 즐겁게 따라와 주세요!

🌱 작은 성공이 쌓이면, 어느새 성장합니다

오늘처럼 하나씩 직접 따라해보고 작은 성취감을 느껴보세요.

그게 쌓이면 진짜 실력이 됩니다.

개발은 재능보다 꾸준함이 더 중요한 분야랍니다. 저도 그랬고요. 😉

 

여러분의 Django 첫 걸음을 응원합니다! 🚀

다음 글에서는 앱 생성과 URL 연결로 한 걸음 더 나아가볼게요.

반응형

+ Recent posts