기본 개념

Django
Python 웹 프레임워크로, 웹 애플리케이션의 전반적인 백엔드 개발을 돕는 도구입니다. HTML 템플릿 렌더링, ORM(Object-Relational Mapping), 라우팅, 인증 등 웹 개발 전반에 필요한 도구를 제공합니다.

  • 사용 사례: 블로그, 커머스 사이트, 포럼 등.

Django REST Framework (DRF)
Django 위에 추가되는 라이브러리로, RESTful API를 쉽게 개발할 수 있게 도와줍니다. DRF는 JSON 응답, API 인증, 직렬화(Serialization) 등을 기본적으로 지원하며, Django ORM과 잘 통합됩니다.

  • 사용 사례: 모바일 앱 백엔드, 데이터 제공용 API 서버, SPA(Single Page Application) 백엔드.

기능 비교

기능  Django DRF 
템플릿 렌더링 HTML 페이지를 렌더링하는 데 적합 JSON이나 XML 같은 데이터 응답을 제공
직렬화 지원하지 않음 Serializer를 통해 Python 객체를 JSON으로 변환 (반대도 가능)
ORM 지원 Django ORM으로 데이터베이스 연동 Django ORM을 기반으로 직렬화와 통합
라우팅 URL 패턴 매핑 (장고의 urlpatterns) API 전용 라우터 (routers) 제공
인증 기본 인증 시스템 (세션, 사용자 관리) 토큰 기반 인증(JWT), OAuth 등 다양한 인증 방식 지원
목적 전통적인 웹 애플리케이션 개발 API 개발 전용
브라우저에서의 디버깅 기본 Django Admin 제공 API를 테스트할 수 있는 Browsable API UI 제공

코드 예시 비교

Django로 구현 (HTML 렌더링)

사용자 정보를 보여주는 HTML 페이지 작성.

# views.py
from django.shortcuts import render
from .models import User

def user_list(request):
    users = User.objects.all()
    return render(request, 'user_list.html', {'users': users})

# urls.py
from django.urls import path
from .views import user_list

urlpatterns = [
    path('users/', user_list, name='user_list'),
]

# user_list.html
<!DOCTYPE html>
<html>
  <body>
    <ul>
      {% for user in users %}
        <li>{{ user.name }}</li>
      {% endfor %}
    </ul>
  </body>
</html>

결과: /users/로 접속하면 사용자 리스트를 보여주는 HTML 페이지가 렌더링됩니다.

 

DRF로 구현 (JSON API)

사용자 정보를 제공하는 RESTful API 작성.

# views.py
from rest_framework.views import APIView
from rest_framework.response import Response
from .models import User
from .serializers import UserSerializer

class UserListAPIView(APIView):
    def get(self, request):
        users = User.objects.all()
        serializer = UserSerializer(users, many=True)
        return Response(serializer.data)

# serializers.py
from rest_framework import serializers
from .models import User

class UserSerializer(serializers.ModelSerializer):
    class Meta:
        model = User
        fields = ['id', 'name', 'email']

# urls.py
from django.urls import path
from .views import UserListAPIView

urlpatterns = [
    path('api/users/', UserListAPIView.as_view(), name='user_list_api'),
]

 

결과: /api/users/로 요청을 보내면 JSON 형식으로 사용자 데이터를 응답합니다

[
  {"id": 1, "name": "Alice", "email": "alice@example.com"},
  {"id": 2, "name": "Bob", "email": "bob@example.com"}
]

Django와 DRF를 함께 사용하는 이유

Django는 HTML 기반 웹 애플리케이션을 빠르게 개발하는 데 강력하지만, JSON과 같은 데이터를 주고받는 API 개발에는 적합하지 않습니다. DRF는 이를 보완하여 Django의 강력한 ORM과 인증 기능을 활용하면서도 RESTful API를 간단히 설계할 수 있도록 합니다.

  • Django로 전통적인 웹 페이지와 관리자(Admin) 페이지를 개발하고,
  • DRF로 모바일 앱이나 프론트엔드(SPA)와 통신하는 API를 추가 개발하는 방식으로 두 도구를 결합할 수 있습니다.

DRF의 장점

  1. 간편한 직렬화: Serializer 클래스로 데이터를 직렬화/역직렬화하는 작업이 쉽습니다.
  2. API 문서화: 자동으로 브라우저에서 테스트 가능한 UI 제공 (Browsable API).
  3. 다양한 인증 방식 지원: OAuth2, JWT 등.
  4. 필터링과 페이징: 데이터의 검색, 정렬, 페이지네이션 기능 내장.
  5. 확장성: 커스터마이징 가능한 강력한 구조.

6. 어떤 것을 선택해야 할까?

  • HTML 페이지 중심의 프로젝트라면 Django만으로 충분.
  • 모바일 앱 또는 React/Vue.js 같은 SPA와 통신할 API가 필요하다면 Django + DRF 조합 추천.

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

패키지 매니저(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을 사용하는 것이 유리할 수 있습니다. 하지만 둘 다 매우 유사하므로, 개인의 선호나 프로젝트 요구 사항에 따라 선택하면 된다.

텍스트를 작성할 수 있는 노트앱을 만들어 볼겁니다.!! 

이 앱은 처음에는 이렇게 아무것도 없는 빈공백에 버튼만 놓여져 있고 이 버튼을 누르게 되면 텍스트를 작성할 수 있는 박스가 생성됩니다.

이 박스에서는 텍스트만 쓸 수 있고 내용을 삭제할 수도 있습니다. 

이렇게 만들어 질 수 있고 다양한 어플들에서 이러한 메모장을 생성해서 어플을 만들었다는 기억이 납니다 .

자, 그럼 이 어플은어떻게 생성되었는지 한번 확인해 보도록 하겠습니다. 


const notesContainer = document.querySelector(".notes-container");
const createBtn = document.querySelector(".btn");
let notes = document.querySelectorAll(".input-box");

- noteContainer는 노트들이 추가될 컨테이너 역할을 합니다. 

- createBtn은 class가 btn인 요소를 참조합니다. 

- notes는 class가 input-box인 <p>요소들을 선택하여 참조합니다. 

 

function showNotes() {
  notesContainer.innerHTML = localStorage.getItem("notes");
}

showNotes();

 이 함수는 로컬스토리지에서 저장된 노트를 불러와서 notesContainer에 표시하는 함수입니다. 

localStorage.getItem('notes')는 로컬 스토리지에서 'notes'라는 키로 저장된 값을 가져옵니다. 

이 값은 notesContainer의 innerHTML로 설정되어 페이지가 로드될 때 저장된 노트들이 표시되도록 합니다.

 

showNotes()를 호출하여 페이지가 로드될 때 자동으로 저장된 노트들이 화면에 표시됩니다.

function updateStorage() {
  localStorage.setItem("notes", notesContainer.innerHTML);
}

updateStorate함수는 현재 notesContainer의 내용을 로컬 스토리지에 저장하는 역할을 합니다. localStorage.setIem('notes',notesContainer.innerHTML)는 notesContainer의 현재 innerHTML을 'notes'라는 로컬 스토리지에 저장합니다. 이를 토통해 페이지를 새로고침해도 노트가 유지됩니다. 

 

createBtn.addEventListener("click", () => {
  let inputBox = document.createElement("p");
  let img = document.createElement("img");
  inputBox.className = "input-box";
  inputBox.setAttribute("contenteditable", "true");
  img.src = "images/delete.png";
  notesContainer.appendChild(inputBox).appendChild(img);
});

이 코드는 createBtn이 클릭되면 새로운 노트를 생성하는 역할을 합니다. 

- inputbox는 새로운 <p>요소를 생성하며 class를 input-box로 설정하고 contentededitable속성을 true로 설정한다. 이 속성은 사용자가 해당 요소를 클릭하여 내용을 편집할 수 있게 해준다. 

- img는 살제버튼으로 사용할 이미지를 생성한다. 이 이미지는 나중에 노트를 삭제할 때 사용된다. 

- 마지막으로 생성된 inputbox와 img를 notesContainer에 추가한다. 이로써 새로운 노트가 화면에 나타나게 된다. 

 

notesContainer.addEventListener("click", function (e) {
  if (e.target.tagName === "IMG") {
    e.target.parentElement.remove();
    updateStorage();
  } else if (e.target.tagName === "P") {
    notes = document.querySelectorAll(".input-box");
    notes.forEach((nt) => {
      nt.onkeyup = function () {
        updateStorage();
      };
    });
  }
});

 이 코든느 notesContainer내에서 발생하는 클릭 이벤트를 처리한다.

- e.target.tagName==='IMG'는 만약 클릭한 요소가 이미지(IMG)라면 해당 이미지를 포함하는 부모요소를 제거한다. 그런다음 updateStorage()를 호출하여 변경된 내용을 로컬 스토리지에 저장한다. 

- e.target.tagName==='p'는 만약 클릭한 요소가 p태그라면 모든 노트요소를 다시 선택하고 각각의 노트에 대해 keyup이벤트를 등록한다. 이 이벤트는 사용자가 노트의 내용을 수정할 때마다 updateStorage()를 호출하여 변경된 내용을 로컬 스토리지에 저장한다. 

document.addEventListener("keydown", (event) => {
  if (event.key === "Enter") {
    document.execCommand("insertLineBreak");
    event.preventDefault();
  }
});

이 코드는 사용자가 노트(inputBox) 안에서 Enter 키를 누를 때 발생하는 동작을 정의합니다.

- 기본적으로 Enter 키를 누르면 새로운 노트가 생성되거나 현재 노트의 내용이 변동될 수 있지만, 이 코드는 document.execCommand("insertLineBreak")를 사용하여 대신 줄 바꿈을 삽입합니다

- event.preventDefault()는 기본 동작을 막아, Enter 키를 누를 때 새 노트가 생성되거나 다른 원치 않는 동작이 발생하지 않도록 합니다.

'개발 > javascript' 카테고리의 다른 글

[JS] 인용구 생성기(quotable generator)  (1) 2024.09.13
[JS]나이 계산기  (0) 2024.09.10
[JS]랜덤 패스워드 생성기!!  (1) 2024.09.09
[JS]퀴즈앱 (quiz app)만들기  (0) 2024.09.06
[JS] To do List 만들기 !  (0) 2024.09.05

+ Recent posts