Kubernetes/OpenShift Local Persistent Volume(Local PV)란 무엇인가?

Kubernetes 또는 OpenShift에서 애플리케이션의 데이터를 영구적으로 저장하기 위해 Persistent Volume(PV)을 사용한다.

PV는 여러 스토리지 타입을 사용할 수 있는데, 그 중 하나가 Local Persistent Volume(Local PV) 이다.

Local PV는 이름 그대로 특정 노드의 로컬 디스크(Local Storage) 를 그대로 PV로 사용하는 방식이다.

외부 스토리지(NFS, Ceph, SAN 등)를 사용하지 않아도 되며, 빠르고 단순한 구성이 가능하다.


1. Local PV는 어떤 저장소인가?

Local PV는 OpenShift/Kubernetes 노드 내부의 실제 디스크 경로를 PV로 등록한 것이다.

예시:

local:
  path: /mnt/local-storage/aibee-dev-shared

이 경로는 Bastion이나 다른 서버가 아닌,

정확히 해당 PV가 배치된 노드의 파일시스템 경로이다.

즉,

+-------------------+
| OpenShift Node #3 |
|   /mnt/local-storage/aibee-dev-shared  ← PV 실제 위치 |
+-------------------+

이 구조로 저장된다.


2. Local PV의 장점

1) 매우 빠른 성능 (가장 큰 장점)

Local PV는 네트워크를 거치지 않고 디스크 I/O에 직접 접근하기 때문에

NFS, CephFS, Cloud Storage보다 훨씬 빠르다.

  • 낮은 latency
  • 높은 throughput
  • SSD 사용 시 특히 고성능

고성능 워크로드에서 많이 사용된다.

2) 구성 단순성

  • 별도 스토리지 서버(NFS, SAN 등) 필요 없음
  • 노드 디스크만 있으면 바로 PV로 사용 가능
  • 테스트/개발 환경에서 특히 빠르게 구성 가능

3) 네트워크 문제 영향 없음

NFS처럼 네트워크 장애로 인해 스토리지가 먹통 되는 일이 없다.


3. Local PV의 단점

1) 노드 종속성 (가장 큰 단점)

Local PV는 특정 노드에 붙어 있기 때문에 그 노드가 다운되면:

  • 해당 PV도 접근 불가
  • 해당 PV를 사용하는 Pod는 다른 노드로 이동 불가
  • 노드 복구 전까지 서비스 영향 발생

즉,

노드 = PV = Pod

이 세 개는 강하게 묶여 있다.

2) 여러 Pod가 공유할 수 없음

Local PV는 RWO(ReadWriteOnce) 가 기본이다.

즉,

  • 여러 Pod가 동시에 접근 불가
  • 여러 노드에서 공유 불가

공유 스토리지가 필요한 경우 Local PV는 적합하지 않다.

3) 운영 관리 부담

서버가 늘어나면 스토리지 경로도 서버마다 따로 관리해야 하므로

운영 난이도가 증가할 수 있다.


4. Local PV vs NFS PV 비교

항목 Local PV NFS PV

저장 위치 특정 노드의 로컬 디스크 외부 NFS 서버
성능 매우 빠름 비교적 느림(네트워크 경유)
Pod 재배치 불가능 (노드 종속) 가능
공유(RWX) 불가 가능
고가용성 낮음 설정에 따라 달라짐
적합한 용도 DB, 캐시, 고성능 처리 이미지 레지스트리, 공유 파일, 공통 데이터

5. 실제 OpenShift에서의 Local PV 구성 방식

OpenShift 또는 Kubernetes에서는 Local Storage Operator를 사용하여 노드 로컬 디스크를 자동으로 PV로 등록한다.

이때 PV는 아래처럼 보인다.

spec:
  local:
    path: /mnt/local-storage/gpu-dev-pv

이 경로는 해당 PV가 존재하는 OpenShift 노드 안에 실제로 존재하는 디렉토리이며,

Bastion과는 전혀 무관하다.


6. Local PV를 선택해야 하는 경우

  • 빠른 I/O가 필요한 애플리케이션(DB, 로그/ETL, AI 중간 데이터)
  • 노드 장애 리스크를 감당할 수 있는 워크로드
  • 임시 데이터(scratch space)
  • 개발, 테스트 환경에서 빠르게 구성할 때

7. Local PV가 적합하지 않은 경우

  • 여러 Pod에서 파일을 공유해야 하는 경우
  • Pod가 다른 노드로 자유롭게 이동해야 하는 경우
  • 높은 가용성이 필요한 프로덕션 워크로드

결론

Local PV = 특정 노드의 로컬 디스크를 PV로 사용하는 방식이며, Bastion 저장공간과는 전혀 무관하다.

속도는 매우 빠르지만, 노드에 종속되기 때문에 신중하게 사용해야 한다.

특히 공유 스토리지 목적이라면 NFS가 필요하고,

고성능 로컬 I/O 목적이라면 Local PV가 더 적합하다.

'Kubernetes' 카테고리의 다른 글

20_NTP (Network Time Protocol)  (0) 2025.11.28
19_OpenShift NFS (Network File System)  (0) 2025.11.28
18_OpenShift에서 헷갈리는 용어 정리  (0) 2025.11.24
16_Volume  (0) 2025.06.25
15_Depolyment MySQL  (0) 2025.06.25

NTP 서버란 무엇인가?

서버, 클라우드, Kubernetes/OpenShift 같은 분산 환경을 운영하면 “시간 동기화(time sync)”라는 개념이 필수적으로 따라옵니다.

이때 사용되는 대표적인 기술이 NTP(Network Time Protocol) 입니다.

이 글에서는 NTP 서버가 무엇인지, 왜 필요한지, 그리고 인프라 환경에서 어떻게 활용되는지를 정리합니다.


1. NTP 서버란 무엇인가

NTP 서버는 네트워크에 있는 모든 컴퓨터와 장비가 동일하고 정확한 시간을 사용하도록 시간 정보를 제공하는 서버입니다.

각 서버는 주기적으로 NTP 서버에 현재 시간을 요청하고, 자신이 가진 시스템 시간을 자동으로 보정합니다.


2. 왜 NTP가 중요한가

분산 시스템에서는 “정확한 시간”이 핵심입니다. 시간이 틀리면 시스템 자체가 오동작할 수 있습니다.

1) 로그 분석

서버, 애플리케이션, 클러스터 로그는 모두 “시간” 기준으로 정렬됩니다.

시간이 어긋나면 사건의 순서를 파악할 수 없습니다.

2) 인증 및 보안 토큰

TLS 인증서, JWT 토큰 등은 만료 시간을 기반으로 동작합니다.

시간이 정확하지 않으면 인증이 실패합니다.

3) Kubernetes/OpenShift 안정성

Kubernetes는 리더 선출·Pod 스케줄링·etcd 동기화 등

모든 내부 동작이 시간에 의존합니다.

시간 불일치 시 발생 가능한 문제:

  • etcd 클러스터 분리
  • 리더 선출 실패
  • Pod가 제때 생성되지 않음
  • 로그·모니터링 시스템 시간 오류

4) 데이터베이스 동기화

PostgreSQL, MySQL, MongoDB 등은 WAL/replication에서

시간을 기반으로 복제를 제어합니다.


3. NTP 서버는 어디에 존재하나

NTP 서버는 다음과 같은 형태로 운영됩니다.

1) 공용 NTP 서버

2) 기업 내부 NTP 서버

보안 및 네트워크 정책 때문에 내부망에서 직접 운영하는 경우가 많습니다.

3) 클라우드 제공 NTP

  • AWS: 169.254.169.123
  • GCP: metadata.google.internal

4) 클러스터 노드 자체에 chrony 설정

대부분 Linux 서버는 chrony 또는 systemd-timesyncd를 사용합니다.


4. NTP의 동작 방식 (간단 버전)

전체 흐름은 다음과 같습니다.

[표준 시간 서버] → [NTP Server] → [OpenShift/K8s Nodes] → [Pods]

노드들은 주기적으로 NTP Server와 통신하여

현재 시간을 비교하고 자신의 시간을 조금씩 조정합니다.


5. OpenShift에서 NTP가 중요한 이유

OpenShift와 Kubernetes는 분산 시스템이기 때문에

모든 노드의 시간이 일치하지 않으면 장애가 발생합니다.

대표적인 문제 사례:

  • API Server 인증 실패
  • etcd 데이터 불일치
  • 로그 timestamp 오류
  • Prometheus/Elastic 수집 시간 왜곡
  • Pod 이벤트 순서 오류

따라서 OpenShift 클러스터 구성 시 NTP 설정은 필수 요소입니다.


6. NTP 상태 확인 방법 (chrony 기준)

동기화 서버 목록 보기

chronyc sources

시간 동기화 상태 확인

chronyc tracking


7. 정리

NTP 서버는 네트워크 전체가 동일한 정확한 시간을 사용하도록 시간 정보를 제공하는 서버입니다.

특히 Kubernetes, OpenShift, 클라우드, 데이터베이스 같은 분산 환경에서는

시간 차이가 발생하면 시스템 자체가 정상 동작할 수 없기 때문에

NTP 설정이 운영 안정성의 핵심 요소입니다.

'Kubernetes' 카테고리의 다른 글

21_Local PV  (0) 2025.11.28
19_OpenShift NFS (Network File System)  (0) 2025.11.28
18_OpenShift에서 헷갈리는 용어 정리  (0) 2025.11.24
16_Volume  (0) 2025.06.25
15_Depolyment MySQL  (0) 2025.06.25

OpenShift 환경에서 애플리케이션이 사용하는 영구 스토리지(Persistent Volume, PV)는 대개 외부 스토리지 서버를 기반으로 구성됩니다. 그 중 가장 널리 사용되는 방식이 NFS(Network File System) 를 이용한 PV 구성입니다.

이 글에서는 Bastion → NFS Server → PV/PVC → Pod 로 이어지는 운영 패턴을 중심으로 설명합니다.


1. NFS Server는 무엇인가

NFS Server는 네트워크를 통해 특정 디렉토리를 공유하고, 다른 서버나 애플리케이션(Pod)이 이를 로컬 디스크처럼 사용할 수 있도록 제공하는 파일 서버입니다.

OpenShift에서는 이 NFS Server가 PV의 실제 저장공간을 담당합니다.


2. Bastion 서버의 역할

Bastion은 OpenShift 클러스터 운영을 위해 마련된 접근 관문 서버입니다.

역할은 다음 두 가지로 요약됩니다.

  1. 관리자가 SSH/SCP로 접근하는 경유지(Jump Host)
  2. NFS Server에 파일을 업로드하기 위한 중간 지점

즉, Bastion은 스토리지 서버가 아닙니다.

PV 데이터는 Bastion이 아니라 NFS Server에 저장됩니다.


3. 전체 구조

OpenShift에서 NFS 기반 PV를 사용하는 구조는 아래 그림처럼 구성됩니다.

[관리자 PC]
      ↓ SSH/SCP
[Bastion]
      ↓ SCP/rsync
[NFS Server]  ← PV의 실제 저장공간
      ↓ PV/PVC
[OpenShift Node]
      ↓
[Pod] → /mnt/data (NFS가 마운트된 디렉토리)

핵심은 다음 한 문장으로 정리됩니다.

"관리자는 Bastion을 통해 NFS Server에 파일을 올리고, Pod은 이 NFS 디렉토리를 PV로 마운트하여 사용한다."


4. 파일 이동은 어떻게 이루어지는가?

1) Bastion → NFS Server로 파일 업로드

관리자는 Bastion 서버에서 NFS Server로 파일을 복사합니다.

예:

scp model.pkl user@nfs-server:/srv/nfs/ml-models/

이때 업로드된 파일은 NFS Server에 저장되며, 해당 디렉토리는 PV에 연결되어 있습니다.


2) OpenShift는 NFS 경로를 PV로 등록

예시 PV 설정(YAML):

apiVersion: v1
kind: PersistentVolume
metadata:
  name: nfs-pv
spec:
  capacity:
    storage: 50Gi
  accessModes:
    - ReadWriteMany
  nfs:
    server: nfs-server
    path: /srv/nfs/ml-models

이 PV는 PVC를 통해 Pod에 마운트됩니다.


3) Pod에서 파일 사용

Pod 내부에서는 PV가 특정 폴더로 마운트된 상태입니다.

oc exec -it my-pod -- ls /mnt/ml-models

→ Bastion → NFS Server로 업로드한 파일이 그대로 보입니다.

Pod은 파일을 “전송”하는 것이 아니라 이미 마운트된 디렉토리를 그대로 읽고 쓰는 방식으로 작동합니다.

 

'Kubernetes' 카테고리의 다른 글

21_Local PV  (0) 2025.11.28
20_NTP (Network Time Protocol)  (0) 2025.11.28
18_OpenShift에서 헷갈리는 용어 정리  (0) 2025.11.24
16_Volume  (0) 2025.06.25
15_Depolyment MySQL  (0) 2025.06.25

 

 

NodePod

 

  • Node = 물리/가상 서버 한 대
    (CPU, 메모리, 디스크, 네트워크 리소스를 제공하는 머신)
  • OpenShift는 보통 다음과 같이 구성됩니다:
    1. Master(Node) / Control Plane Node
      • API Server, Scheduler, Controller Manager가 실행되는 노드
      • Pod는 실행되지만 일반적으로 system pod 위주
    2. Worker Node
      • 사용자의 Pod, Deployment, StatefulSet 등이 실행되는 곳
      • GPU Worker Node 같은 GPU 리소스 전용 노드도 존재 가능

 

  • Pod = Node 위에서 실행되는 애플리케이션의 최소 단위
    (컨테이너 1개 또는 여러 개를 포함하는 실행 단위)
    • 최소 배포 단위

 


1. Bastion Server(점프 서버)란?

Bastion 서버는 다음과 같은 용도로 사용됩니다:

  • Cluster Node(Master/Worker)에 직접 SSH로 접근하지 않도록
    중간 단계(Jump Host)에서만 접근하도록 보안 통제
  • 외부(사내망 밖)에서 클러스터 내부로 들어가기 위한 유일한 서버
  • 관리자가 Node에 접근할 때 거치는 보안 게이트웨이

즉, 관리자 → Bastion → Node 형태로 접근합니다.

 

 

Bastion은 Node 자체가 아니다

반복해서 명확히 구분하면:

  • Node
    • Pod가 실행되는 서버
    • OpenShift 클러스터의 구성 요소
    • kubelet, CRI-O, network daemon 등이 작동
    • 클러스터의 일부
  • Bastion
    • 클러스터 바깥에 있는 별도의 서버
    • Node들로 들어가는 SSH 관문
    • 보안강화를 위한 접속점
    • Pod가 실행되지 않음
    • 클러스터의 일부가 아님 (일반적으로)


Namespace

1. Node = 물리/가상 서버 한 대

(당신이 이미 정확히 이해한 내용)

예:

  • Worker Node 1 (서버 1대)
  • Worker Node 2 (서버 1대)
  • Master Node 1 (서버 1대)

Node는 서버입니다.
Pod가 실행되는 공간입니다.

 

2. Cluster = Node 여러 개의 묶음

즉, 서버 여러 대를 합친 전체 시스템입니다.

예:

 
Cluster
├── Master Node 1 (서버)
├── Master Node 2 (서버)
└── Worker Node 1 (서버)
  • Kubernetes 클러스터
  • OpenShift 클러스터
    둘 다 “Node 여러 개의 집합”이라는 뜻입니다.

Node ≠ Cluster
Node는 클러스터의 “구성 요소”일 뿐입니다.

 

3. Namespace는 Node와 아무 관계가 없음

Namespace는 “Node를 나누는 것”이 아닙니다.
Namespace는 클러스터 내부의 Kubernetes 리소스를 논리적으로 나누는 구역입니다.

Namespace는 “서버(Node)”를 나누는 것이 아니라

  • Pod
  • Service
  • Deployment
  • ConfigMap
  • Secret
  • PVC

같은 Kubernetes 리소스의 논리적 공간을 나누는 것입니다.

즉:

Namespace는 소프트웨어 레벨의 논리적 분리
Node는 하드웨어(서버)

완전히 다른 개념입니다.

 

4. 혼동을 풀기 위한 전체적 구조 그림

 

 

Namespace는 논리적으로 리소스 사용량을 관리하는 가상의 구획입니다.

즉, Namespace는 리소스를 실제로 제공하지 않지만, “얼마까지 사용 가능한지”를 제한할 수 있다.

예:

  • dev namespace는 CPU 10개까지만 사용 가능
  • prod namespace는 CPU 100개까지 허용
  • monitoring namespace는 메모리 8GB까지만 허용

이런 식으로, Namespace는 “클러스터 정책”에 관여할 뿐

 

 

 

 

 

 

 

 

 

'Kubernetes' 카테고리의 다른 글

20_NTP (Network Time Protocol)  (0) 2025.11.28
19_OpenShift NFS (Network File System)  (0) 2025.11.28
16_Volume  (0) 2025.06.25
15_Depolyment MySQL  (0) 2025.06.25
14_Secret  (0) 2025.06.24

LINK

위의 강의를 기반으로 작성

React 코드를 다루기 전에 알면 좋은 개념을 정리해보겠습니다.

 

1. 컴포넌트란 무엇인가?

재사용이 가능한 각각의 독립된 모듈을 의미

  • 때에 따라 콘텐츠만 다르게 입력해주면 일관적인 UI 표현 가능.
    • 글자, 색깔과 같은 것들만 바꿔서 사용하면 일관적이게 사용가능.
  • 코드 재사용으로 인해 유연하게 소프트웨어 시스템 구축 가능.
  • React 개발의 경우 컴포넌트를 개발하는 것과 동일한 개념.

 

컴포넌트의 역할

  • 데이터 관리: 외부에서 주입받은 데이터나 상태와 같은 내부 데이터 관리.
  • UI 그리기: 데이터가 사용자에게 어떻게 보여질지 정의.
  • 사용자와 상호작용: 클릭과 같이 사용자와 어떻게 상호작용 할 지.

 

2. Props

Props의 정의

  • 근본적으로 Props는 부모 컴포넌트에서 자식 컴포넌트로 전달할 수 있는 사용자 정의 속성.

특징

  • 컴포넌트 내부에서 관리되며, 자식 컴포넌트의 관점에서 불변.
  • 부모 컴포넌트에 의해 설정되며 자식 컴포넌트에 의해 변경되지 않음.
  • Props는 데이터와 이벤트 핸들러를 부모 컴포넌트로부터 자식 컴포넌트로 전달하는데 사용.
  • 단방향 데이터 흐름 설정.

※ Props 더 깊게 알아보기!

더보기

React는 함수에서 인자를 받을때 객체 형태로 한 번에 들어옵니다.

그래서 원래는 아래와 같이 인자를 받게 됩니다.

function Card(props) {
	return <div>{props.title}<div/>
}

// 사용
<Card title="Hello" />

→ 여기서 props는 {title: "Hello"} 라는 객체

 

하지만 매번 props.title 과 같이 쓰기 귀찮아서 아래와 같이 꺼내서 사용하게 됩니다.

function Card({title}) {
	return <div>{title}<div />
}

 

만약 여러 개의 인자를 받는다면 아래와 같이 사용됩니다.

function Card({title, color, onClick}) {
	return (
    	<div style={{ backgroundColor: color}} onClick={onClick}>
        	{title}
        <div />
    )
}

// 사용
<Card title="Hello" color="skyblue" onClick={() => alert("clicked")} />

 

위와 같이 여러 값들을 받게 된다면 어떤 값들을 받는지 미리 정의해주기 위해 Props를 사용합니다.

type CardProps = {
  title: string
  color: string
  onClick: () => void
}

function Card({ title, color, onClick }: CardProps) {
  return (
    <div style={{ backgroundColor: color }} onClick={onClick}>
      {title}
    </div>
  )
}

 

 

3. 상태관리 Store

상태 관리 Store란?

  • 상태 (State)
    • 컴포넌트 내부에서 관리되며 애플리케이션의 렌더에 영향을 미치는 자바스크립트 객체.
    • 즉, 쉽게 말하면 변화하는 데이터.
  • 상태 관리
    • 상태 (State)가 컴포넌트마다 다르면 유저, 관리자가 받아들이는 데이터에 차이가 생겨 소통에 차질.
    • 상태 값은 일관되게 관리해야 하며, 이를 위해 중앙집중식 상태관리라는 개념 존재.

 

Store의 역할

 

Props를 사용하여 각 컴포넌트에 데이터를 전달하게 되는 행위가 계속 반복된다면 여러가지 문제가 발생합니다.

이를 위해 Store라는 개념이 도입되었습니다.

 

4. Recoil

Recoil이란?

  • React를 위한 상태 관리 라이브러리.
  • Atoms, Selectors 개념 존재.

 

Recoil 사용 방법

  • Atom 란?
    • Atom은 상태 (state)의 일부를 표현.
    • Atom는 어떤 컴포넌트에서나 읽고 쓰기 가능.
    • Atom에 어떤 변화가 있으면 그 atom을 구독하는 모든 컴포넌트가 재렌더링.
  • Selectors 란?
    • 파생된 상태 (derived state)의 일부를 표현.
    • 파생된 상태는 기존 상태의 변화.
      • 예, atom에 "Hello"가 있으면, selector에서 글자 수를 세거나 대문자로 바꾼 값을 자동으로 만들어줌.
    • atom에 "원시 데이터"만 저장하고, 화면에서 쓰기 좋은 형태는 selector로 변환.

실습 시작

Vite

 

node.js가 설치되어 있다는 가정하에 진행

Vite에 접근하여 프로젝트 생성 명령어 실행

npm create vite@latest

 

 

VSCode의 Extension에 들어가서 ES7 React/Redux/GraphQL...을 설치해줍니다.

VSCode에서 React/Redux/React-Native/GraphQL 코드를 빠르게 타이핑하게 해주는 자동완성 스니펫 모음입니다.

 

진행하기에 앞서 아래의 명령어를 실행해 여러 라이브러리를 설치해줍니다.

 - npm install axios

 - npm install recoil

 - npm install -D sass

 - npm install react-router-dom localforage match-sorter sort-by

 - npm install @types/node

 - npm install react-simple-toasts

 

※ React 최신 19버전은 Recoil이 제대로 작동하지 않아 다운그레이드!

 - npm uninstall react react-dom

 - npm install react@18.2.0 react-dom@18.2.0

더보기

 

※ 사용되는 라이브러리 별 설명 ※

  1. npm: Node.js를 설치하면 같이 딸려오는 패키지 관리자
  2. yarn: npm과 같은일을 하는 또 다른 패키지 관리자
  3. recoil: React에서 쓰는 상태 관리 라이브러리
  4. axios: HTTP 요청을 쉽게 해주는 라이브러리
  5. sass: CSS를 더 편하게 쓰게 해주는 스타일링 언어
  6. react-router-dom: React에서 페이지 이동(라우팅)을 담당하는 라이브러리
  7. localforage: 브라우저 안에 데이터를 저장할 때 사용하는 라이브러리
  8. match-sorter: 검색 기능을 쉽게 구현하게 도와주는 라이브러리
  9. sort-by: 데이터를 특정 기준으로 정렬할 때 사용하는 라이브러리
  10. @types/node: TypeScript에서 Node.js 모듈을 사용 가능하게 해주는 라이브러리
  11. react-simple-toasts: React에서 간단하게 토스트 메시지(알림창)를 띄워주는 라이브러리

 


vite.config.fs 수정하기 

vite.config.fs 파일에서는 alias를 설정해줄 수 있습니다.

: 여기서 alias를 지정해줄 수 있습니다.

이렇게 함으로써 특정 경로에 짧은 별명을 붙여 사용할 수 있습니다.

 

 : 모든 SCSS 파일에 특정 SCSS 파일을 자동으로 import 해주는 설정.

 


tsconfig.app.json 수정하기

 

"baseUrl": "."
모든 경로 해석의 기준(base)을 정해줍니다.
"."이면, 현재 프로젝트 루트(tsconfig.app.json이 있는 위치)를 기준으로 삼겠다는 뜻이에요.
즉, src/ 같은 상대경로를 루트 기준으로 깔끔하게 해석할 수 있습니다.

 

"paths": { ... }
TypeScript에서 alias(경로 별명)을 지정하는 부분.

TypeScript도 이해 가능.

 

"noImplicitAny"

타입이 없는 건 그냥 any로 두고 넘어가자”라는 의미.

 

"strictnullChecks"

null/undefined를 엄격하게 체크할지 여부.

false면 모든 타입에 null과 undefined를 묵시적으로 허용.

 


실행해보기

이제 간단하게 실행을 시켜보기 위한 셋팅을 하겠습니다.

 

src 폴더 내에 있는 index.css와 main.css를 삭제해주고,

main.tsx의 import 중 삭제한 파일들을 지워줍니다.

다음으로 App.tsx로 들어가서 모두 다 지운 뒤 rfce를 입력 후 Enter를 누르면 아래와 같이 자동완성이 됩니다.

 

div 사이에 "App컴포넌트 입니다."를 입력하고 npm run dev를 입력 후 제공된 URL로 접속하면

오른쪽과 같은 화면을 볼 수 있습니다.


Main 페이지 만들기

먼저 src에 pages라는 폴더를 생성해 줍니다.

index.tsx 파일을 생성해주고 아래와 같이 코드를 입력해줍니다.

 

다음으로는 실제로 실행될 App.tsx 파일에 가서 아래와 같이 코드를 수정해줍니다.

 

 - BrowserRouter : 라우팅 (페이지 이동)을 가능하게 해주는 최상위 컴포넌트.

 - Routes : 여러 개의 Route들을 묶어 관리하는 컨테이너.

 - Route : 실제로 어떤 경로(path)일 떄 어떤 화면 (element)를 보여줄지를 정하는 컴포넌트.

 

실행하면 아래와 같은 메인 페이지를 볼 수 있습니다.


About 페이지 만들기

main 페이지를 만들때와 같이 src 폴더에 about.tsx 파일을 생성해줍니다.

 

그리고 App.tsx 파일에 <Route>를 추가해 줍니다.

단, 추가를 해줄때 path를 "/about"이라고 설정해줍니다.

 

이대로 실행시켜보고, localhost:5173/about으로 접속해주면 아래와 같은 화면을 볼 수 있습니다.

 


assets/styles/main.scss 활용하기

먼저 진행을 하기 전에 폴더 구조를 아래와 같이 바꿔서 진행해줍니다.

 

언급이 없던 파일들은 빈 파일로 생성해 놓은 상태.

 

먼저 assets 폴더 부터 셋팅을 해주겠습니다.

assets 폴더의 styles에는

  • color.scss : 프로젝트 전반에 사용될 색들의 정의
    • 더보기
      // COLOR WHITE
      $color-white-000: #ffffff;
      $color-white-100: #ececec;
      $color-white-200: #f2f4f6;
      $color-white-300: #d9d9d9;
      $color-white-400: #a8a8a8;
      $color-white-500: #9c9c9b;
      $color-white-600: #777777;
      $color-white-700: #667085;

      // COLOR GRAY
      $color-gray-000: #fcfcfd;
      $color-gray-050: #f9fafb;
      $color-gray-100: #f2f4f7;
      $color-gray-200: #e4e7ec;
      $color-gray-300: #d0d5dd;
      $color-gray-400: #98a2b3;
      $color-gray-500: #667085;
      $color-gray-600: #475467;
      $color-gray-700: #344054;
      $color-gray-800: #1d2939;
      $color-gray-900: #101828;

      // COLOR ERROR
      $color-error-000: #fffbfa;
      $color-error-050: #fef3f2;
      $color-error-100: #fee4e2;
      $color-error-200: #fecdca;
      $color-error-300: #fda29b;
      $color-error-400: #f97066;
      $color-error-500: #f04438;
      $color-error-600: #d92d20;
      $color-error-700: #b4231b;
      $color-error-800: #912018;
      $color-error-900: #7a271a;

      // COLOR WARNING
      $color-warning-000: #fffcf5;
      $color-warning-050: #fffaeb;
      $color-warning-100: #fef0c7;
      $color-warning-200: #fedf89;
      $color-warning-300: #fec84b;
      $color-warning-400: #fdb022;
      $color-warning-500: #f79009;
      $color-warning-600: #dc6803;
      $color-warning-700: #b54708;
      $color-warning-800: #93380d;
      $color-warning-900: #7a2e0e;

      // COLOR SUCCESS
      $color-success-000: #f6fef9;
      $color-success-050: #ecfdf3;
      $color-success-100: #d1fadf;
      $color-success-200: #a6f4c5;
      $color-success-300: #6ce9a6;
      $color-success-400: #32d583;
      $color-success-500: #12b76a;
      $color-success-600: #039855;
      $color-success-700: #027a48;
      $color-success-800: #05603a;
      $color-success-900: #054f31;

      // COLOR PINK
      $color-pink-000: #fef5fb;
      $color-pink-050: #fdf2fa;
      $color-pink-100: #fce7f6;
      $color-pink-200: #fcceee;
      $color-pink-300: #faa7e0;
      $color-pink-400: #f670c7;
      $color-pink-500: #ee46bc;
      $color-pink-600: #dd2590;
      $color-pink-700: #c11574;
      $color-pink-800: #9e165f;
      $color-pink-900: #851651;

      // COLOR ROSE
      $color-rose-000: #fff5f6;
      $color-rose-050: #fff1f3;
      $color-rose-100: #ffe4e8;
      $color-rose-200: #fecdd6;
      $color-rose-300: #fea3b4;
      $color-rose-400: #fd6f8e;
      $color-rose-500: #f63d68;
      $color-rose-600: #e31b54;
      $color-rose-700: #c01048;
      $color-rose-800: #a11043;
      $color-rose-900: #89123e;

      // COLOR ORANGE
      $color-orange-000: #fffaf5;
      $color-orange-050: #fff6ed;
      $color-orange-100: #ffead5;
      $color-orange-200: #fddcab;
      $color-orange-300: #feb273;
      $color-orange-400: #fd853a;
      $color-orange-500: #fb6514;
      $color-orange-600: #ec4a0a;
      $color-orange-700: #c4320a;
      $color-orange-800: #9c2a10;
      $color-orange-900: #7e2410;

      // COLOR BLUE LIGHT
      $color-lightBlue-000: #f5fbff;
      $color-lightBlue-050: #f0f9ff;
      $color-lightBlue-100: #e0f2fe;
      $color-lightBlue-200: #b9e6fe;
      $color-lightBlue-300: #7cd4fd;
      $color-lightBlue-400: #36bffa;
      $color-lightBlue-500: #0ba5ec;
      $color-lightBlue-600: #0086c9;
      $color-lightBlue-700: #026aa2;
      $color-lightBlue-800: #065986;
      $color-lightBlue-900: #0b4a6f;

      // COLOR BLUE
      $color-blue-000: #f5faff;
      $color-blue-050: #eff8ff;
      $color-blue-100: #d1e9ff;
      $color-blue-200: #b2ddff;
      $color-blue-300: #84caff;
      $color-blue-400: #53b1fd;
      $color-blue-500: #2e90fa;
      $color-blue-600: #1570ef;
      $color-blue-700: #175cd3;
      $color-blue-800: #1849a9;
      $color-blue-900: #194185;

      // COLOR INDIGO
      $color-indigo-000: #f5f8ff;
      $color-indigo-050: #eef4ff;
      $color-indigo-100: #e0eaff;
      $color-indigo-200: #c7d7fe;
      $color-indigo-300: #a4bcfd;
      $color-indigo-400: #8098f9;
      $color-indigo-500: #6172f3;
      $color-indigo-600: #444ce7;
      $color-indigo-700: #3538cd;
      $color-indigo-800: #2d31a6;
      $color-indigo-900: #2d3282;

      // COLOR PUPPLE
      $color-pupple-000: #fafaff;
      $color-pupple-050: #f4f3ff;
      $color-pupple-100: #ebe9fe;
      $color-pupple-200: #d9d6fe;
      $color-pupple-300: #bd84fe;
      $color-pupple-350: #aaa0ff;
      $color-pupple-400: #9b8afb;
      $color-pupple-500: #7a5af8;
      $color-pupple-600: #6938ef;
      $color-pupple-700: #5925dc;
      $color-pupple-750: #6157b8;
      $color-pupple-800: #4a1fb8;
      $color-pupple-900: #3e1c96;

      // COLOR BLACK
      $color-black-900: #000000;
  • font.scss : 프로젝트 전반에 사용될 폰트들의 정의
  • main.scss : color.scss, font.scss를 한번에 묶어주는 css 파일
    • vite.config.ts 파일에서 additionalData로 정의를 해주었기 때문에 프로젝트 어디에서도 사용할 수 있다.

위와 같이 구성됩니다.

 

다음은 pages 폴더를 보겠습니다.

pages 폴더에는 실제로 구성할 페이지들을 위한 파일들이 있습니다.

index 폴더의 styles 폴더를 보면 index.module.scss가 있습니다.

index.module.scss는 index.tsx에서 사용될 css 파일입니다.

내용은 아래와 같습니다.

$HEADER-HEIGHT: 50px;
$FOOTER-HEIGHT: 50px;
$NAVIGATION-HEIGHT: 50px;


.page {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: flex-start;

  width: 100%;
  height: 100vh;

  &__contents {
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: flex-start;

    width: 100%;
    height: calc(100% - $HEADER-HEIGHT - $NAVIGATION-HEIGHT - $FOOTER-HEIGHT);

    &__introBox {
      display: flex;
      align-items: center;
      justify-content: center;

      width: 100%;
      height: 40%;

      background-image: url('/src/assets/images/image-intro.jpg');
      background-size: cover;
      background-position: 100% 15%;
      
      .wrapper {
        display: flex;
        flex-direction: column;

        &__title {
          margin-bottom: 4px;

          color: main.$color-white-000;
          font-size: 3rem;
          font-weight: 700;
        }
        &__desc {
          color: main.$color-white-000;
          margin-bottom: 32px;
        }
      }
    }
    &__imageBox {
      display: flex;
      align-items: center;
      justify-content: center;

      width: 100%;
      height: 60%;
      flex-wrap: wrap;

      padding: 16px 60px;

      gap: 16px;

      overflow-y: scroll;
    }
  }
}

여기서 background-image: url()은 아무 이미지나 다운 받은 후에 사용하면 됩니다.

위의 css 파일을 사용한 index.css는 아래와 같습니다.

import styles from "./styles/index.module.scss"

function index() {
  return (
    <div className={styles.page}>
      {/* 공통 헤더 UI 부분 */}
      {/* 공통 네비게이션 UI 부분 */}
      <div className={styles.page__contents}>
        <div className={styles.page__contents__introBox}>
          <div className={styles.wrapper}>
            <span className={styles.wrapper__title}>PhotoSplash</span>
            <span className={styles.wrapper__desc}>
              인터넷의 시각 자료 출처입니다. <br />
              모든 지역에 있는 크리에이터들의 지원을 받습니다.
            </span>
            {/* 검색창 UI 부분 */}
          </div>
        </div>
        <div className={styles.page__contents__imageBox}></div>
      </div>
      {/* 공통부터 UI 부분 */}
    </div>
  )
}

export default index

이를 실행시켜보면 아래와 같은 화면을 볼 수 있습니다

 

 


공통 Component (Page 전반적으로 사용될 Header 만들기)

공통 Component를 사용하기 위해 Header를 만들어 주겠습니다.

먼저, src 폴더에 components/common 폴더를 만들어 주고 하위 폴더로 header, navigation, searchBar를 만들어 줍니다.

그리고 위의 이미지와 같이 header 폴더에 CommonHeader.module.scss와 CommonHeader.tsx를 생성해 줍니다.

 

먼저 CommonHeader.tsx로 구성을 짜주기로 합니다.

 

그리고 Commonheader.module.scss 파일을 아래와 같이 만들어 줍니다.

 

마지막으로 해당 Header를 사용하기 위해 src/pages 폴더 내 index.tsx 파일에 <CommonHeader />를 추가해줍니다.

 

그러면 아래와 같이 화면에 Header가 추가된 것을 볼 수 있습니다.


AXIOS를 활용하여 API 연결해서 실행하기 - Props 개념 포함

src/pages/index.tsx에서 API를 호출하여 Card에서 볼 수 있게 진행하겠습니다.

먼저 Props이라는 개념에 대해 설명하겠습니다.

  • Props :
    • 부모 컴포넌트에서 자식 컴포넌트로 데이터를 전달하는 작업.
    • 해당 프로젝트에서는 index.tsx 파일에서 API를 호출하여 데이터 조회 후 Card 컴포넌트에서 사용.
    • Props의 깊이가 깊어질 수록 데이터를 추적하기 힘들기 때문에 공통/전역에서 사용하는 데이터의 경우에는 중앙집중식으로 관리를 위해 Recoil을 통해 관리.

 


API를 사용하기 위해 사전 준비를 하겠습니다.

Google에 unsplash라고 검색 후 사이트에 접속해줍니다.

 

그리고 위의 순서를 따라 사용할 수 있는 API Access 키를 받아줍니다.

받아온 API Access 키를 사용해보겠습니다.

 

먼저 src/pages/index/index.tsx로 들어가줍니다.

그 후에 아래와 같이 코드를 작성해줍니다.

 

그리고 페이지에 접속하여 개발자 모드의 console을 확인하면 아래와 같이 30개의 데이터를 가져온 것을 알 수 있습니다.

 

API를 통해 어떤 식으로 데이터를 가져오는지 확인을 했고, 위의 데이터를 활용하기 위해 DTO를 정의해줍니다.

src/pages/index/types/card.ts 를 생성해줍니다.

그리고 위에서 가져온 result를 기반으로 DTO를 아래와 같이 만들어 줍니다.

export interface CardDTO {
  alt_description: string
  alternative_slugs: any
  asset_type: string
  blur_hash: string
  bookmarked: boolean
  breadcrumbs: []
  color: string
  created_at: string
  current_user_collections: []
  description: string
  height: number
  id: string
  liked_by_user: boolean
  likes: number
  links: Link
  promoted_at: string
  slug: string
  sponsorship: string
  topic_submissions: any 
  updated_at: string
  urls: Url
  user: any
  width: number
}

interface Link {
  download: string
  download_location: string
  html: string
  self: string
}

interface Url {
  full: string
  raw: string
  regular: string
  small: string
  small_s3: string
  thumb: string
}

 

그리고 위의 데이터를 사용하기 위해 Card.tsx (Card 컴포넌트 파일)을 아래와 같이 수정해줍니다.


Recoil 사용하기

Recoil은 위에서 설명했던 것과 같이 컴포넌트의 상태 관리를 원활히 하기 위해 사용됩니다.

단, React 19 버전 이상 부터는 Recoil이 정상적으로 동작하지 않다는 말이 많습니다.

그래서 React 19버전을 사용하려면 Recoil에서 파생된 다른 상태 관리 라이브러리를 사용하는 것이 좋을 것 같습니다.

 

먼저 Recoil을 사용하기 위해서는 src 폴더의 App.tsx 파일로 가서 코드를 아래와 같이 수정을 합니다.

 

Recoil은 전역으로 상태를 관리 라이브러리 입니다.

그래서 전역에서 사용하려면 컴포넌트 트리 전체에 공급하기 위해 최상위 컴포넌트로 사용합니다.

 

recoil을 사용하기 위해 src/recoil 폴더를 생성해줍니다.

그리고 atoms, selectors 폴더를 각각 하위 폴더로 생성해줍니다.

 

atom으로 사용할 요소는 index.tsx에서 사용했던 searchValue, pageValue 입니다.

 

왼쪽은 searchState.ts, 오른쪽은 pageState.ts 입니다.

 

그리고 searchVectorpageValue를 통하여 이미지를 가져오는 부분을 selector로 생성해서 사용하면 됩니다.

selector 폴더의 imageSelector.ts 파일을 아래와 같이 만들어 줍니다.

 

 

마지막으로 recoil을 src/pages/index/index.jsx에 적용을 해보도록 하겠습니다.

※ 기존 수정 전 코드

더보기
function index() {
  // imgUrls의 초기값으로 빈 배열인 []로 선언
  const [imgUrls, setImgUrls] = useState([])

  const getData = async () => {
    // 오픈 API 호출
    const API_URL = 'https://api.unsplash.com/search/photos'
    const API_KEY = 'NbAEhQjAJYjXeb18uIWli6Fj4WQCsg2TQ5xYJjDllqc'
    const PER_PAGE = 30

    const searchValue = 'Korea'
    const pageValue = 100

    try {
      const res = await axios.get(`${API_URL}?query=${searchValue}&client_id=${API_KEY}&page=${pageValue}&per_page=${PER_PAGE}`)

      if (res.status == 200) {
        setImgUrls(res.data.results)
      }

      console.log(res)
    } catch (error) {
      console.log(error)
    }
  }

  const cardList = imgUrls.map((card: CardDTO) => {
    return <Card data={card} key={card.id} />
  })
  
  useEffect(() => {
    getData()
  }, [])

  return (
    <div className={styles.page}>
      {/* 공통 헤더 UI 부분 */}
      <CommonHeader />
      {/* 공통 네비게이션 UI 부분 */}
      <CommonNav />
      <div className={styles.page__contents}>
        <div className={styles.page__contents__introBox}>
          <div className={styles.wrapper}>
            <span className={styles.wrapper__title}>PhotoSplash</span>
            <span className={styles.wrapper__desc}>
              인터넷의 시각 자료 출처입니다. <br />
              모든 지역에 있는 크리에이터들의 지원을 받습니다.
            </span>
            {/* 검색창 UI 부분 */}
            <CommonSearchBar />
          </div>
        </div>
        <div className={styles.page__contents__imageBox}>
          {
            cardList
          }
        </div>
      </div>
      {/* 공통부터 UI 부분 */}
      <CommonFooter />
    </div>
  )
}

※ recoil 적용 index.jsx 파일

import CommonHeader from '@components/common/header/CommonHeader'
import CommonSearchBar from '@components/common/searchBar/CommonSearchBar'
import CommonNav from '@components/common/navigation/CommonNav'
import CommonFooter from '@/components/common/footer/CommonFooter'
import Card from './components/Card'
// CSS
import styles from "./styles/index.module.scss"
// TYPE
import type { CardDTO } from './types/card.ts'
import { useRecoilValue } from 'recoil'
import { imageData } from '@/recoil/selectors/imageSelector.ts'

function index() {
  const imgSelector = useRecoilValue(imageData)

  const CARD_LIST = imgSelector.data.results.map((card: CardDTO) => {
    return <Card data={card} />
  })

  return (
    <div className={styles.page}>
      {/* 공통 헤더 UI 부분 */}
      <CommonHeader />
      {/* 공통 네비게이션 UI 부분 */}
      <CommonNav />
      <div className={styles.page__contents}>
        <div className={styles.page__contents__introBox}>
          <div className={styles.wrapper}>
            <span className={styles.wrapper__title}>PhotoSplash</span>
            <span className={styles.wrapper__desc}>
              인터넷의 시각 자료 출처입니다. <br />
              모든 지역에 있는 크리에이터들의 지원을 받습니다.
            </span>
            {/* 검색창 UI 부분 */}
            <CommonSearchBar />
          </div>
        </div>
        <div className={styles.page__contents__imageBox}>
          {
            CARD_LIST
          }
        </div>
      </div>
      {/* 공통부터 UI 부분 */}
      <CommonFooter />
    </div>
  )
}

export default index

 

기존 코드와 비교하였을때 굉장히 간략해졌습니다.

먼저 만들어 둔 selector인 imageData를 import 해줍니다.

또 React에서 selector를 사용하기 위한 useRecoilValue를 import 해주고 아래와 같이 작성을 해줍니다.

그리고 이미지를 잘 불러오는지 새로고침하여 페이지를 확인하면 됩니다.

 

 

 

 

 

 

 

 

 

 

'Web > frontend' 카테고리의 다른 글

02_CSS_2  (1) 2025.10.27
02_CSS  (0) 2025.10.27
01_HTML  (0) 2025.10.27
01_React  (0) 2025.09.24

LINK

 

1. 박스 모델

웹 페이지에서 요소가 차지하는 공간을 설명하는 모델.

각 HTML 요소는 content (내용), padding (패딩), borer (테두리), margin (마진) 이라는 네 가지 주요 부분으로 구성된다.

 

  • Content: 영역 주변의 내부 여백으로, 텍스트나 내용과 테부리 사이의 공간.
  • Border: 테두리 주변의 외부 여백으로, 요소와 주변 요소 사이의 간격. 요소 간 간격을 조절하는데 사용.

 


2. 레이아웃과 포지셔닝

 

 - 블록 요소

부모 요소의 가로폭 전체를 차지하며, 세로로 쌓인다.

기본적으로 새로운 줄에서 시작한다.

div, p, h1~h6, ul, ol, li, form 등이 블록 요소에 해당.

 

 - 인라인 요소

컨텐츠의 일부분만 차지하며, 요소가 포함한 콘텐츠의 크기만큼만 가로폭을 갖는다.

새로운 줄에서 시작하지 않고, 줄 중간에 추가된다.

width, height, margin-top, margin-bottom, padding-top, padding-bottom, text-decoration 등의 속성이 설정되도 무시될 수 있다.

span, a, strong, em, img 등이 인라인 요소에 해당.

 

 

 - Display 속성

주요 Display 속성 값

block: 해당 요소를 블록 레벨 요소로 지정.

inline: 해당 요소를 인라인 요소로 지정.

inline-block:  인라인 요소처럼 취급되면서도 블록 레벨 요소처럼 너비와 높이를 설정 가능.

none: 해당 요소를 화면에서 숨김.

 

 

 - flexbox

Flexbox는 유연한 박스 모델을 통해 아이템을 배치하고 정렬하는 데 사용되는 CSS 레이아웃 방식.

Flexbox를 사용하면 복잡한 레이아웃을 간단하게 처리할 수 있으며,

동적이고 유연한 디자인을 만들 수 있다.

Flexbox는 주로 한 줄의 레이아웃을 조절하는 데 사용한다.

수평 축 또는 수직 축 중 하나를 선택하여 아이템들을 배치한다.

Test LINK

# 만약 class 이름이 container인 요소들을 flex하게 변경할 경우

.container {
	display: flex;
    flex-direction: column; -> 수직
    flex-wrap: wrap; -> 창을 조절해도 크기에 맞게 수정
    justify-content: start; -> 맨 왼쪽으로 붙게 됨, end or center로 변경 가능
    gap: 10px 5px; -> 요소들 간의 행과 열 사이의 크기 조절
}

 

 

 - grid

2차원 레이아웃 시스템

.container {
    display: grid;
    grid-template-columns: 200px 200px; -> 줄을 2개로 하고 싶다면 200px 200px, 3개면 200px 하나 더 추가
    grid-template-rows: 1fr 2fr;
    juustify-items: center;
    align-items: center;
}

 

 

 

 

 

'Web > frontend' 카테고리의 다른 글

04_React  (0) 2025.10.28
02_CSS  (0) 2025.10.27
01_HTML  (0) 2025.10.27
01_React  (0) 2025.09.24

1. CSS란?

"Cascading Style Sheets"의 약자로, HTML이나 XML로 작성된 문서의 스타일을 꾸미는 언어이다.

HTML은 웹 페이지의 구조를 정의하고, CSS는 그 구조에 스타일을 부여한다.

기본적으로 웹 페이지의 레이아웃, 색상, 글꼴, 크기 및 간격과 같은 시각적인 표현을 담당한다.

 

선택자 (Selector)와 선언 (Declaration)으로 구성되어 있다.

선택자스타일을 적용할 HTML 요소를 선택하는 역할을 하고,

선언선택된 요소에 적용할 스타일 속성과 값을 나타낸다.

 


2. 선택자와 속성

  • 태그 선택자

  • 클래스 선택자

  • 아이디 선택자

  • 결합 선택자

 - 자식 선택자 (Child Selector)

직계 자식 요소를 선택

html
css

css 파일을 보면 div > p라고 적혀있는 것을 볼 수 있습니다.

'>' 뜻은 div의 직계 자식 요소를 선택하라는 뜻입니다.

 

 

 - 인접 형제 선택자 (Adjacent Sibling Selector)

특정 요소의 인접한 형제 요소를 선택.

이렇게 되면 h2와 인접한 <p>첫번째 단락</p>만 바뀌는 것을 볼 수 있습니다.

 

 

 - 일반 형제 선택자 (General Sibling Selector)

특정 요소의 모든 형제 요소를 선택하는데 사용.

지정한 요소와 같은 부모를 가진 모든 요소를 선택합니다.

 

 

 - 속성 선택자 (Attribute Selector)

태그에 [속성] 형식으로 속성 이름을 넣어주면 해당 속성을 가진 요소를 선택.

 

 

 

 - 가상 클래스 선택자 (Pseudo-Class Selector)

특정 상태에 있는 요소를 선택하는데 사용.


3. CSS의 Cascading

 

"Cascading"은 "차례차례로 적용되는"이라는 의미를 갖고 있다.

이 용어는 스타일 규칙이 어떻게 적용되고 충돌을 해결하는지를 나타낸다.

CSS는 특정한 규칙을 따른다.

 

Specificity (특이도): 스타일 규칙이 얼마나 특정한지를 나타냅니다.

더 구체적인 규칙이 더 높은 특이도를 가지며 우선적으로 적용된다.

 

Importance (중요도): Importance 키워드가 지정된 스타일 규칙은 다른 모든 규칙보다 높은 중요도를 가진다.

 

Source Order (출처 순서): 나중에 나오는 규칙일수록 먼저 나온 규칙을 덮어쓴다.

'Web > frontend' 카테고리의 다른 글

04_React  (0) 2025.10.28
02_CSS_2  (1) 2025.10.27
01_HTML  (0) 2025.10.27
01_React  (0) 2025.09.24

LINK

 

1. HTML이란?

 - HTML은 "Hypertext Markup Language"의 약자로, 웹 페이지를 만들기 위한 표준 마크업 언어

 - 텍스트 기반으로 문서의 구조를 정의하고, 컨텐츠를 표시하는 역할

 

 

2 .HTML의 기본 구조

<!DOCTYPE html>
<html>
    <head>
    	<title></title>
    </head>
	
    <body>
    </body>
</html>

 

  • <!DOCTYPE html>
    • 현재 문서가 어떤 html 버전으로 작성되었는지 알려준다.
    • 웹 표준을 준수하고 브라우저 간 호환성을 향상. 렌더링 하는데 도움이 된다.
    • 검증과 디버깅에 도움이 된다. 브라우저가 예상한 코드 방식과 다르면 브라우저에서 경고를 표시.
    • 렌더링이란?
      데이터나 코드를 시각적으로 표현하는 과정. 주로 HTML, CSS, JavaScript가 사용된다.
  • <html>
    • HTML 문서의 시작.
    • 모든 html 요소는 이 태그 안에 포함.
  • <head>
    • 문서에 대한 부가적인 정보.
    • 문서의 메타데이터와 스타일, 스크립트 링크 등
  • <body>
    • 웹 페이지의 본문 내용을 포함.
    • 실제 콘텐츠가 위치.

 

 

3. 웹 페이지 <body>의 구성요소

  • <nav>
    • HTML 문서에서 네비게이션 링크를 정의하는데 사용된다.
    • 주로 웹 페이지의 메뉴, 링크 목록 등을 감싸는 데에 활용.
  • <section>
    • 문서의 섹션.
    • 주로 같은 주제나 주제 그룹에 속하는 콘텐츠를 묶을 때 사용.
  • <footer>
    • 웹 페이지나 섹션의 푸터를 정의.
    • 보통 저작권 정보, 연락처 정보, 관련 링크 등
  • <main>
    • 웹 페이지의 주요 콘텐츠를 정의.
    • 한 페이지에 하나만 사용.
  • <aside>
    • 사이드바, 광고, 콜아웃 박스 등.
    • 본문과는 독립적으로 표시되는 부가적인 콘텐츠 정의.
  • <p>
    • 단락으로써 여러 문장이나 내용.
  • <br>
    • 줄 바꿈 태그.
  • <hr>
    • 구분선.
  • 목록 태그
    • <ul>: 순서가 없는 목록.
    • <ol>: 순서가 있는 목록.
    • <li>: 목록 항목.
  • <a>
    • 링크를 붙여주는 태그.
  • <img>
    • 이미지를 붙여넣어 줄 수 있는 태그.
  • <div> - 큰 레이아웃 그룹화
    • Division의 약자로, 블록 수준의 컨테이너 요소.
    • 여러 요소들을 그룹화하거나 레이아웃을 나눌 때 사용.
  • <span> - 텍스트 내 작은 부분
    • 주로 텍스트 내에서 작은 부분을 감싸거나 스타일을 적용하기 위해 사용.
    • 인라인 수준의 요소이기 때문에 같은 줄에 텍스트와 함께 표시.
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>div & span 컨테이너 태그</title>
</head>
<body>
  
  <div style="color: aqua;">
    <p>현재 블록 레벨 컨테이너 안에 있습니다.</p>
    <p>이것은 <span style="color: red; font-size:30px;">인라인 레벨</span> 컨테이너 입니다.</p>
    <p>다른 요소들을 그룹화 할때 사용됩니다.</p>
  </div>

</body>
</html>

 

 

4. 간단한 양식(<form>) 태그

  • 텍스트 입력

  • 라디오 버튼

  • 체크박스

 

  • 드롭다운

  • 텍스트 에어리어

 

  • 복합 사용

'Web > frontend' 카테고리의 다른 글

04_React  (0) 2025.10.28
02_CSS_2  (1) 2025.10.27
02_CSS  (0) 2025.10.27
01_React  (0) 2025.09.24

+ Recent posts