Angular 기초 (Angular Basics)

Angular 기초 (Angular Basics)

Angular는 강력한 풀스택 프론트엔드 프레임워크다. 타입스크립트와 컴포넌트 기반 구조로 대규모 애플리케이션에 적합하다. 이번에는 Angular의 기본부터 심화까지 코드와 함께 자세히 풀어보려고 한다.


Angular를 잘 이해하면 구조화된 웹 앱을 효율적으로 만들 수 있다. 하나씩 단계별로 알아보자.


Angular 시작하기

Angular를 사용하려면 먼저 Angular CLI로 프로젝트를 생성한다:

# Angular CLI 설치
npm install -g @angular/cli

# 새 프로젝트 생성
ng new my-app
cd my-app
ng serve

// src/app/app.component.html
<h1>안녕, Angular!h1>

ng serve로 앱을 실행하면 기본 템플릿이 표시된다.


1. 컴포넌트 생성

Angular는 컴포넌트로 UI를 구성한다. 새 컴포넌트를 만들어보자:

# 컴포넌트 생성
ng generate component greeting

// src/app/greeting/greeting.component.ts
import { Component } from '@angular/core';

@Component({
    selector: 'app-greeting',
    template: `

안녕, {{ name }}!

`
}) export class GreetingComponent { name = '홍길동'; } // src/app/app.component.html <app-greeting>app-greeting>

@Component 데코레이터로 컴포넌트를 정의하고 사용했다.


2. 데이터 바인딩

Angular는 다양한 데이터 바인딩을 제공한다:

// src/app/app.component.ts
import { Component } from '@angular/core';

@Component({
    selector: 'app-root',
    template: `
        
        

입력값: {{ text }}

`
}) export class AppComponent { text = ''; } // src/app/app.module.ts import { FormsModule } from '@angular/forms'; @NgModule({ imports: [FormsModule] })

[(ngModel)]로 양방향 바인딩을 구현했다.


3. 디렉티브 활용

Angular의 디렉티브로 동적인 UI를 만든다:

// src/app/app.component.ts
@Component({
    template: `
        
        

보이는 상태

`
}) export class AppComponent { isVisible = false; toggle() { this.isVisible = !this.isVisible; } }

*ngIf로 조건을, (click)으로 이벤트를 처리했다.


4. 리스트 렌더링

*ngFor로 리스트를 렌더링한다:

// src/app/app.component.ts
@Component({
    template: `
        
  • {{ item }}
`
}) export class AppComponent { items = ['사과', '바나나', '오렌지']; }

*ngFor로 배열을 순회하며 표시했다.


5. 서비스와 의존성 주입

서비스로 데이터를 관리한다:

# 서비스 생성
ng generate service data

// src/app/data.service.ts
import { Injectable } from '@angular/core';

@Injectable({ providedIn: 'root' })
export class DataService {
    getItems() {
        return ['사과', '바나나'];
    }
}

// src/app/app.component.ts
import { DataService } from './data.service';

@Component({
    template: `
  • {{ item }}
`
}) export class AppComponent { items: string[]; constructor(private dataService: DataService) { this.items = dataService.getItems(); } }

의존성 주입으로 서비스를 컴포넌트에 연결했다.


6. HTTP 요청

API 데이터를 가져와보자:

// src/app/app.module.ts
import { HttpClientModule } from '@angular/common/http';
@NgModule({
    imports: [HttpClientModule]
})

// src/app/app.component.ts
import { HttpClient } from '@angular/common/http';

@Component({
    template: `
  • {{ post.title }}
`
}) export class AppComponent { posts: any[] = []; constructor(private http: HttpClient) { this.http.get('https://jsonplaceholder.typicode.com/posts') .subscribe(data => this.posts = (data as any).slice(0, 5)); } }

HttpClient로 데이터를 가져와 렌더링했다.


7. 성능과 구조에 미치는 영향

Angular가 앱에 어떤 영향을 주는지 보자:

- 성능: 변경 감지로 효율적이지만, 초기 로드가 약간 무거울 수 있다.

- 구조: 타입스크립트와 모듈로 대규모 앱에 강하다.

컴포넌트서비스가 Angular의 강력함을 보여준다.


마무리

Angular는 컴포넌트, 바인딩, 서비스를 통해 구조화된 웹 개발을 가능하게 한다. 기본부터 HTTP 요청까지 다채롭게 활용할 수 있다.


Vue.js 기초 (Vue.js Basics)

Vue.js 기초 (Vue.js Basics)

Vue.js는 간단하면서도 강력한 프론트엔드 프레임워크다. 데이터 바인딩과 컴포넌트 구조로 UI를 쉽게 만들어준다. 이번에는 Vue.js의 기본부터 심화까지 코드와 함께 자세히 풀어보려고 한다.


Vue.js를 잘 이해하면 반응형 인터페이스를 빠르게 구현할 수 있다. 하나씩 단계별로 알아보자.


Vue.js 시작하기

Vue.js를 사용하려면 먼저 CDN으로 추가하거나 프로젝트를 설정한다. 간단히 CDN으로 시작해보자:

<html>
<head>
    <script src="https://unpkg.com/vue@3/dist/vue.global.js">script>
head>
<body>
    <div id="app">
        {{ message }}
    div>
    <script>
        const { createApp } = Vue;
        createApp({
            data() {
                return { message: "안녕, Vue.js!" };
            }
        }).mount("#app");
    script>
body>
html>
// "안녕, Vue.js!"

{{ }}로 데이터를 화면에 바로 표시했다.


1. 데이터 바인딩

Vue.js의 핵심은 양방향 데이터 바인딩이다:

<div id="app">
    <input v-model="text">
    <p>입력값: {{ text }}p>
div>

<script>
    const { createApp } = Vue;
    createApp({
        data() {
            return { text: "" };
        }
    }).mount("#app");
script>

v-model로 입력값과 데이터를 실시간으로 연결했다.


2. 디렉티브 사용

Vue.js는 디렉티브로 동적인 UI를 만든다:

<div id="app">
    <button v-on:click="toggle">토글button>
    <p v-if="isVisible">보이는 상태p>
div>

<script>
    const { createApp } = Vue;
    createApp({
        data() {
            return { isVisible: false };
        },
        methods: {
            toggle() {
                this.isVisible = !this.isVisible;
            }
        }
    }).mount("#app");
script>

v-if로 조건을, v-on으로 이벤트를 처리했다.


3. 리스트 렌더링

v-for로 배열을 렌더링할 수 있다:

<div id="app">
    <ul>
        <li v-for="item in items" :key="item">{{ item }}li>
    ul>
div>

<script>
    const { createApp } = Vue;
    createApp({
        data() {
            return { items: ["사과", "바나나", "오렌지"] };
        }
    }).mount("#app");
script>
// 
  • 사과
  • 바나나
  • 오렌지

:key로 각 항목을 고유하게 식별했다.


4. 컴포넌트 만들기

Vue.js에서도 컴포넌트로 UI를 분리할 수 있다:

<div id="app">
    <item-card v-for="item in items" :key="item" :name="item">item-card>
div>

<script>
    const { createApp } = Vue;
    const app = createApp({
        data() {
            return { items: ["사과", "바나나"] };
        }
    });

    app.component("item-card", {
        props: ["name"],
        template: `

항목: {{ name }}

`
}); app.mount("#app"); script>

props로 데이터를 전달해 컴포넌트를 재사용했다.


5. 반응형 상태

Vue 3에서는 refreactive로 반응형 데이터를 만든다:

<div id="app">
    <p>카운트: {{ count }}p>
    <button v-on:click="increment">증가button>
div>

<script>
    const { createApp, ref } = Vue;
    createApp({
        setup() {
            const count = ref(0);
            const increment = () => { count.value++; };
            return { count, increment };
        }
    }).mount("#app");
script>

ref로 반응형 변수를 정의하고 버튼으로 값을 변경했다.


6. 데이터 가져오기

API 데이터를 가져와 렌더링해보자:

<div id="app">
    <ul>
        <li v-for="post in posts" :key="post.id">{{ post.title }}li>
    ul>
div>

<script>
    const { createApp, ref, onMounted } = Vue;
    createApp({
        setup() {
            const posts = ref([]);
            onMounted(async () => {
                const res = await fetch("https://jsonplaceholder.typicode.com/posts");
                posts.value = (await res.json()).slice(0, 5);
            });
            return { posts };
        }
    }).mount("#app");
script>

onMounted로 마운트 시 데이터를 가져왔다.


7. 성능과 구조에 미치는 영향

Vue.js가 앱에 어떤 영향을 주는지 보자:

- 성능: 반응형 시스템으로 필요한 부분만 업데이트된다.

- 구조: 직관적인 디렉티브와 컴포넌트로 코드가 깔끔해진다.

데이터 바인딩디렉티브가 Vue.js의 강점이다.


마무리

Vue.js는 데이터 바인딩, 디렉티브, 컴포넌트를 통해 UI 개발을 단순화한다. 기본부터 데이터 가져오기까지 유연하게 활용할 수 있다.


Node.js 기초 (Node.js Basics)

Node.js 기초 (Node.js Basics)

Node.js는 자바스크립트를 브라우저 밖에서 실행할 수 있게 해주는 런타임이다. 비동기 I/O와 이벤트 기반 아키텍처로 서버 개발에 강력한 도구로 자리잡았다. 이번에는 Node.js의 기본부터 심화까지 코드와 함께 자세히 풀어보려고 한다.


Node.js를 잘 다루면 서버와 백엔드 로직을 효율적으로 구현할 수 있다. 하나씩 단계별로 알아보자.


Node.js 시작하기

Node.js를 사용하려면 먼저 설치해야 한다. 설치 후 간단한 코드를 실행해보자:

// hello.js
console.log("안녕, Node.js!");

# 터미널에서 실행
node hello.js
// "안녕, Node.js!"

Node.js가 설치되어 있다면 node 명령어로 파일을 바로 실행할 수 있다.


1. 모듈 시스템

Node.js는 모듈로 코드를 분리해 관리한다. 기본 모듈을 사용해보자:

// math.js
function add(a, b) {
    return a + b;
}

module.exports = { add };

// main.js
const math = require("./math");
console.log(math.add(2, 3));
// 5

module.exports로 함수를 내보내고, require로 불러왔다.


2. 파일 읽고 쓰기

fs 모듈로 파일 시스템을 다룰 수 있다:

const fs = require("fs");

// 파일 쓰기
fs.writeFileSync("test.txt", "Hello, Node.js!");

// 파일 읽기
const data = fs.readFileSync("test.txt", "utf8");
console.log(data);
// "Hello, Node.js!"

동기 방식으로 파일을 읽고 썼다. 비동기 방식도 가능하다.


3. 비동기 처리

Node.js의 강점은 비동기 처리다. fs 모듈의 비동기 메서드를 사용해보자:

const fs = require("fs");

fs.readFile("test.txt", "utf8", (err, data) => {
    if (err) {
        console.log("에러: " + err);
        return;
    }
    console.log(data);
});
// "Hello, Node.js!"

콜백으로 비동기 작업 결과를 처리했다.


4. 간단한 서버 만들기

http 모듈로 웹 서버를 구축할 수 있다:

const http = require("http");

const server = http.createServer((req, res) => {
    res.writeHead(200, { "Content-Type": "text/plain" });
    res.end("안녕, Node.js 서버!");
});

server.listen(3000, () => {
    console.log("서버가 3000번 포트에서 실행 중");
});

3000번 포트에서 실행하면 브라우저에서 응답을 확인할 수 있다.


5. Express로 서버 개선

express를 사용하면 서버 개발이 더 간편해진다:

# 설치
npm install express

// app.js
const express = require("express");
const app = express();

app.get("/", (req, res) => {
    res.send("안녕, Express!");
});

app.listen(3000, () => {
    console.log("Express 서버가 3000번 포트에서 실행 중");
});

Express로 간단한 GET 요청을 처리하는 서버를 만들었다.


6. 미들웨어 사용

Express에서 미들웨어로 요청을 가공할 수 있다:

const express = require("express");
const app = express();

app.use((req, res, next) => {
    console.log("요청 시간: " + new Date());
    next();
});

app.get("/", (req, res) => {
    res.send("미들웨어 테스트");
});

app.listen(3000);

미들웨어로 요청 시 시간을 로그에 남기고 다음 단계로 넘어갔다.


7. REST API 구축

간단한 REST API를 만들어 보자:

const express = require("express");
const app = express();
app.use(express.json());

let items = ["사과", "바나나"];

app.get("/items", (req, res) => {
    res.json(items);
});

app.post("/items", (req, res) => {
    items.push(req.body.name);
    res.json(items);
});

app.listen(3000);

GET으로 리스트를 조회하고, POST로 새 항목을 추가했다.


8. 성능과 활용성

Node.js가 코드에 어떤 영향을 주는지 보자:

- 성능: 비동기 처리로 I/O 작업이 많은 환경에서 뛰어나다.

- 활용성: 자바스크립트로 풀스택 개발이 가능해진다.

비동기모듈 시스템이 Node.js의 핵심이다.


마무리

Node.js는 모듈, 비동기, 서버 구축을 통해 백엔드 개발을 단순화한다. 기본 파일 작업부터 REST API까지 다채롭게 활용할 수 있다.


React 기초 (React Basics)

React 기초 (React Basics)

React는 UI를 컴포넌트 단위로 쌓아 올려 동적인 웹을 만들어내는 라이브러리다. 컴포넌트와 상태 관리를 중심으로 설계되어 효율적이고 유연한 개발을 가능하게 한다. 이번에는 React의 기본부터 심화까지 코드와 함께 자세히 풀어보려고 한다.


React를 잘 이해하면 복잡한 인터페이스도 깔끔하게 구성할 수 있다. 하나씩 단계별로 알아보자.


React 시작하기

React를 사용하려면 먼저 프로젝트를 설정해야 한다. create-react-app을 활용하면 간단하다:

# 터미널에서 실행
npx create-react-app my-app
cd my-app
npm start

// src/App.js 기본 구조
import React from 'react';

function App() {
    return (
        <div>
            <h1>안녕, React!h1>
        div>
    );
}

export default App;

위 코드는 React 앱의 기본 컴포넌트를 보여준다. JSX를 사용해 HTML처럼 작성할 수 있다.


1. 컴포넌트 만들기

React는 컴포넌트를 기반으로 동작한다. 간단한 컴포넌트를 만들어보자:

import React from 'react';

function Greeting(props) {
    return <p>안녕, {props.name}!p>;
}

function App() {
    return (
        <div>
            <Greeting name="홍길동" />
            <Greeting name="김영희" />
        div>
    );
}

export default App;
// "안녕, 홍길동!"
// "안녕, 김영희!"

props로 데이터를 전달해 재사용 가능한 컴포넌트를 만들었다.


2. 상태 관리

React에서 동적인 UI를 위해 useState 훅을 사용한다:

import React, { useState } from 'react';

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

    return (
        <div>
            <p>클릭 횟수: {count}p>
            <button onClick={() => setCount(count + 1)}>증가button>
        div>
    );
}

export default Counter;

useState로 상태를 정의하고, 버튼 클릭 시 상태를 업데이트했다.


3. 이벤트 처리

React에서 이벤트는 JSX 속성으로 처리된다:

import React, { useState } from 'react';

function InputForm() {
    const [text, setText] = useState("");

    const handleChange = (event) => {
        setText(event.target.value);
    };

    return (
        <div>
            <input type="text" value={text} onChange={handleChange} />
            <p>입력값: {text}p>
        div>
    );
}

export default InputForm;

onChange로 입력값을 실시간으로 상태에 반영했다.


4. 조건부 렌더링

React는 조건에 따라 UI를 다르게 렌더링할 수 있다:

import React, { useState } from 'react';

function Toggle() {
    const [isOn, setIsOn] = useState(false);

    return (
        <div>
            <button onClick={() => setIsOn(!isOn)}>토글button>
            {isOn ? <p>켜짐p> : <p>꺼짐p>}
        div>
    );
}

export default Toggle;

삼항 연산자로 상태에 따라 다른 내용을 표시했다.


5. 리스트 렌더링

배열 데이터를 렌더링할 때는 map을 활용한다:

import React from 'react';

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

    return (
        <ul>
            {items.map((item, index) => (
                <li key={index}>{item}li>
            ))}
        ul>
    );
}

export default ItemList;
// 
  • 사과
  • 바나나
  • 오렌지

key 속성을 추가해 React가 리스트를 효율적으로 관리하게 했다.


6. 효과 훅 (useEffect)

useEffect로 컴포넌트의 생명주기를 관리할 수 있다:

import React, { useState, useEffect } 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>;
}

export default Timer;

마운트 시 타이머를 시작하고, 언마운트 시 정리했다.


7. 데이터 가져오기

API 데이터를 가져와 렌더링해보자:

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

function PostList() {
    const [posts, setPosts] = useState([]);

    useEffect(() => {
        fetch("https://jsonplaceholder.typicode.com/posts")
            .then(res => res.json())
            .then(data => setPosts(data.slice(0, 5)));
    }, []);

    return (
        <ul>
            {posts.map(post => (
                <li key={post.id}>{post.title}li>
            ))}
        ul>
    );
}

export default PostList;

fetch로 데이터를 가져와 상태에 저장하고 렌더링했다.


8. 성능과 구조에 미치는 영향

React가 앱에 어떤 영향을 주는지 보자:

- 성능: 가상 DOM으로 변경 사항만 업데이트해 빠르다.

- 구조: 컴포넌트 기반으로 코드가 모듈화되고 관리하기 쉬워진다.

컴포넌트으로 React의 유연성과 효율성이 돋보인다.


마무리

React는 컴포넌트와 상태, 훅을 통해 동적인 UI를 단순화한다. 기본 컴포넌트부터 데이터 가져오기까지 다양한 상황에서 강력하게 활용할 수 있다.


jQuery 기초 (jQuery Basics)

jQuery 기초 (jQuery Basics)

자바스크립트로 DOM을 다룰 때 jQuery를 활용하면 코드가 훨씬 간결하고 직관적이 된다. DOM 조작, 이벤트 처리, 애니메이션, AJAX 호출 등을 쉽게 구현할 수 있게 해주는 라이브러리다. 이번에는 jQuery의 기본부터 심화까지 코드와 함께 자세히 풀어보려고 한다.


jQuery를 잘 이해하면 웹 페이지의 동적 요소를 빠르게 다룰 수 있다. 하나씩 단계별로 알아보자.


jQuery 시작하기

jQuery를 사용하려면 먼저 라이브러리를 페이지에 추가해야 한다. CDN을 활용하는 방법이 간단하다:

<!-- jQuery CDN 추가 -->
<script src="https://code.jquery.com/jquery-3.7.1.min.js"></script>

<!-- 기본 사용 -->
<script>
    $(document).ready(function() {
        console.log("jQuery 준비 완료!");
    });
</script>

$(document).ready는 DOM이 완전히 로드된 후 코드를 실행하게 해준다. 이제 jQuery를 본격적으로 다뤄보자.


1. 선택자와 DOM 조작

jQuery의 핵심은 강력한 선택자다. CSS 스타일 선택자를 사용해 요소를 쉽게 찾을 수 있다:

<div class="box">첫 번째 박스</div>
<div class="box">두 번째 박스</div>

<script>
    $(document).ready(function() {
        const $boxes = $(".box");
        $boxes.css("color", "blue");
        $boxes.text("jQuery로 변경됨");
    });
</script>
// 두 박스의 텍스트가 "jQuery로 변경됨"으로 바뀌고 파란색이 됨

$()로 요소를 선택하고, css()text()로 스타일과 내용을 바꿨다.


2. 이벤트 처리

jQuery로 이벤트 핸들링도 간단하다. 클릭 이벤트를 추가해보자:

<button id="myButton">클릭</button>
<p id="result"></p>

<script>
    $(document).ready(function() {
        $("#myButton").click(function() {
            $("#result").text("버튼이 클릭됐습니다!");
        });
    });
</script>

click() 메서드로 버튼 클릭 시 동작을 정의했다. 이벤트 연결이 매우 직관적이다.


3. 요소 추가와 제거

DOM에 요소를 동적으로 추가하거나 제거할 수도 있다:

<div id="container"></div>
<button id="add">추가</button>
<button id="remove">제거</button>

<script>
    $(document).ready(function() {
        $("#add").click(function() {
            $("#container").append('<p>새 요소</p>');
        });
        $("#remove").click(function() {
            $("#container p").remove();
        });
    });
</script>

append()로 요소를 추가하고, remove()로 삭제했다. DOM 조작이 훨씬 간소화된다.


4. 애니메이션 효과

jQuery는 애니메이션도 쉽게 구현할 수 있다:

<div id="box" style="width: 100px; height: 100px; background: orange;"></div>
<button id="animate">애니메이션</button>

<script>
    $(document).ready(function() {
        $("#animate").click(function() {
            $("#box").animate({
                width: "200px",
                height: "200px",
                opacity: 0.5
            }, 1000);
        });
    });
</script>

animate()로 크기와 투명도를 1초 동안 부드럽게 변화시켰다.


5. AJAX로 데이터 가져오기

jQuery를 사용하면 AJAX 요청도 간단하다:

<button id="fetch">데이터 가져오기</button>
<div id="result"></div>

<script>
    $(document).ready(function() {
        $("#fetch").click(function() {
            $.ajax({
                url: "https://jsonplaceholder.typicode.com/posts/1",
                method: "GET",
                success: function(data) {
                    $("#result").text(data.title);
                },
                error: function() {
                    $("#result").text("데이터를 가져오지 못했습니다.");
                }
            });
        });
    });
</script>

$.ajax로 외부 API에서 데이터를 가져와 화면에 표시했다.


6. 체이닝 활용

jQuery는 메서드 체이닝을 지원해 코드를 간결하게 만든다:

<div class="box">박스</div>

<script>
    $(document).ready(function() {
        $(".box")
            .css("color", "red")
            .text("체이닝 테스트")
            .animate({ fontSize: "24px" }, 1000);
    });
</script>

한 줄에 여러 메서드를 연결해서 순차적으로 적용했다.


7. 이벤트 위임

동적으로 추가된 요소에도 이벤트를 적용하려면 이벤트 위임을 사용한다:

<div id="list">
    <button class="item">항목 1</button>
</div>
<button id="add">항목 추가</button>

<script>
    $(document).ready(function() {
        $("#list").on("click", ".item", function() {
            console.log("항목 클릭됨");
        });
        $("#add").click(function() {
            $("#list").append('<button class="item">새 항목</button>');
        });
    });
</script>

on()으로 상위 요소에 이벤트를 위임해 새로 추가된 요소에도 동작이 적용된다.


8. 플러그인처럼 확장

jQuery는 사용자 정의 메서드를 추가할 수 있다:

<div class="box">테스트 박스</div>

<script>
    $.fn.highlight = function(color) {
        this.css("backgroundColor", color);
        return this;
    };

    $(document).ready(function() {
        $(".box").highlight("yellow");
    });
</script>

$.fn으로 새로운 메서드를 정의하고 호출했다.


9. 성능과 활용성

jQuery가 코드에 어떤 영향을 주는지 살펴보자:

- 성능: 선택자와 체이닝은 빠르지만, 복잡한 DOM 조작이 많아지면 약간의 부하가 생길 수 있다.

- 활용성: 직관적인 API로 빠르게 동적 웹을 만들 수 있다.

$() 선택자와 메서드 체이닝이 jQuery의 강력함을 보여준다.


마무리

jQuery는 DOM 조작, 이벤트, 애니메이션, AJAX를 단순화하며 웹 개발을 더 쉽게 만들어준다. 기본 선택자부터 이벤트 위임, 확장까지 다채롭게 활용할 수 있다.


Canvas API

Canvas API

브라우저에서 그래픽을 그리고 애니메이션을 만들 때 Canvas API는 매우 강력한 도구다. 2D 그래픽을 자유롭게 다룰 수 있어서 게임이나 데이터 시각화에 유용하다. 이번에는 Canvas API의 기본부터 심화된 활용까지 코드와 함께 자세히 알아보려고 한다.


Canvas API를 잘 다루면 복잡한 그래픽 작업도 쉽게 구현할 수 있다. 하나씩 차근차근 살펴보자.


Canvas 기본 설정

<canvas> 요소와 2D 컨텍스트를 사용해서 시작할 수 있다:

const canvas = document.createElement("canvas");
canvas.width = 400;
canvas.height = 300;
document.body.appendChild(canvas);

const ctx = canvas.getContext("2d");
ctx.fillStyle = "#ddd";
ctx.fillRect(0, 0, 400, 300);
// 400x300 크기의 회색 사각형 캔버스 생성

getContext("2d")로 2D 컨텍스트를 얻고, 기본 배경을 그렸다.


1. 기본 도형 그리기

사각형, 선, 원 같은 기본 도형을 그려보자:

const canvas = document.createElement("canvas");
canvas.width = 400;
canvas.height = 300;
document.body.appendChild(canvas);
const ctx = canvas.getContext("2d");

ctx.fillStyle = "red";
ctx.fillRect(50, 50, 100, 100);
// 빨간 사각형

ctx.strokeStyle = "blue";
ctx.lineWidth = 2;
ctx.strokeRect(200, 50, 100, 100);
// 파란 테두리 사각형

ctx.beginPath();
ctx.strokeStyle = "green";
ctx.moveTo(50, 200);
ctx.lineTo(150, 250);
ctx.stroke();
// 초록 선

ctx.beginPath();
ctx.fillStyle = "purple";
ctx.arc(250, 200, 40, 0, Math.PI * 2);
ctx.fill();
// 보라색 원

fillRect, strokeRect, lineTo, arc로 다양한 도형을 그렸다.


2. 경로와 스타일 조합

복잡한 경로를 그리고 스타일을 적용해보자:

const canvas = document.createElement("canvas");
canvas.width = 400;
canvas.height = 300;
document.body.appendChild(canvas);
const ctx = canvas.getContext("2d");

ctx.beginPath();
ctx.strokeStyle = "orange";
ctx.lineWidth = 3;
ctx.moveTo(50, 50);
ctx.lineTo(150, 100);
ctx.lineTo(100, 150);
ctx.closePath();
ctx.fillStyle = "yellow";
ctx.fill();
ctx.stroke();
// 노란색 채우기와 주황색 테두리의 삼각형

beginPathclosePath로 경로를 만들고 채우기와 테두리를 함께 적용했다.


3. 텍스트와 이미지 추가

텍스트와 이미지를 캔버스에 그려보자:

const canvas = document.createElement("canvas");
canvas.width = 400;
canvas.height = 300;
document.body.appendChild(canvas);
const ctx = canvas.getContext("2d");

ctx.font = "30px Arial";
ctx.fillStyle = "black";
ctx.fillText("안녕, Canvas!", 50, 50);
ctx.strokeStyle = "red";
ctx.strokeText("안녕, Canvas!", 50, 100);
// 검은 채우기 텍스트와 빨간 테두리 텍스트

const img = new Image();
img.src = "https://via.placeholder.com/100";
img.onload = () => {
    ctx.drawImage(img, 50, 150);
};
// 100x100 이미지 그리기

fillText, strokeText로 텍스트를 그리고, drawImage로 이미지를 추가했다.


4. 변환과 애니메이션

회전, 이동, 애니메이션을 적용해보자:

const canvas = document.createElement("canvas");
canvas.width = 400;
canvas.height = 300;
document.body.appendChild(canvas);
const ctx = canvas.getContext("2d");

ctx.translate(200, 150);
ctx.rotate(Math.PI / 4);
ctx.fillStyle = "blue";
ctx.fillRect(-50, -50, 100, 100);
// 중앙에서 45도 회전한 파란 사각형

let angle = 0;
function animate() {
    ctx.clearRect(-200, -150, 400, 300);
    ctx.rotate(Math.PI / 180);
    angle += Math.PI / 180;
    ctx.fillStyle = "green";
    ctx.fillRect(-25, -25, 50, 50);
    requestAnimationFrame(animate);
}
animate();
// 계속 회전하는 초록 사각형

translate, rotate로 변환을 적용하고, 애니메이션은 requestAnimationFrame으로 구현했다.


5. 이벤트와 상호작용

마우스 이벤트를 캔버스에 연결해보자:

const canvas = document.createElement("canvas");
canvas.width = 400;
canvas.height = 300;
document.body.appendChild(canvas);
const ctx = canvas.getContext("2d");

canvas.addEventListener("mousemove", (event) => {
    const x = event.offsetX;
    const y = event.offsetY;
    ctx.clearRect(0, 0, 400, 300);
    ctx.beginPath();
    ctx.fillStyle = "orange";
    ctx.arc(x, y, 20, 0, Math.PI * 2);
    ctx.fill();
});
// 마우스를 따라다니는 주황색 원

mousemove 이벤트로 마우스 위치를 추적해서 원을 그렸다.


6. 그림자와 그라디언트

그림자와 그라디언트로 시각적 효과를 더해보자:

const canvas = document.createElement("canvas");
canvas.width = 400;
canvas.height = 300;
document.body.appendChild(canvas);
const ctx = canvas.getContext("2d");

ctx.shadowColor = "gray";
ctx.shadowBlur = 10;
ctx.shadowOffsetX = 5;
ctx.shadowOffsetY = 5;
ctx.fillStyle = "blue";
ctx.fillRect(50, 50, 100, 100);
// 그림자가 있는 파란 사각형

const gradient = ctx.createLinearGradient(200, 50, 300, 150);
gradient.addColorStop(0, "red");
gradient.addColorStop(1, "yellow");
ctx.fillStyle = gradient;
ctx.fillRect(200, 50, 100, 100);
// 빨강에서 노랑으로 변하는 그라디언트 사각형

shadow 속성과 createLinearGradient로 효과를 추가했다.


7. 클리핑과 합성

클리핑 영역과 합성 모드를 사용해보자:

const canvas = document.createElement("canvas");
canvas.width = 400;
canvas.height = 300;
document.body.appendChild(canvas);
const ctx = canvas.getContext("2d");

ctx.beginPath();
ctx.arc(200, 150, 50, 0, Math.PI * 2);
ctx.clip();
ctx.fillStyle = "pink";
ctx.fillRect(0, 0, 400, 300);
// 원형 클리핑 영역에 핑크색 채우기

ctx.globalCompositeOperation = "source-over";
ctx.fillStyle = "rgba(0, 0, 255, 0.5)";
ctx.fillRect(250, 200, 100, 100);
// 반투명 파란 사각형 합성

clip으로 그리기 영역을 제한하고, globalCompositeOperation으로 합성 방식을 조정했다.


8. 성능과 활용성 고려

Canvas API가 작업에 어떤 영향을 주는지 보자:

- 성능: 복잡한 그래픽은 자원을 많이 소모하지만, 애니메이션 최적화로 부담을 줄일 수 있다.

- 활용성: 도형, 텍스트, 이미지, 이벤트까지 다채롭게 조합 가능하다.

컨텍스트 설정requestAnimationFrame을 활용하면 동적인 그래픽을 효율적으로 다룰 수 있다.


마무리

Canvas API는 2D 그래픽을 그리기 위한 강력한 도구다. 기본 도형부터 애니메이션, 이벤트, 효과까지 다루며, 게임이나 시각화 작업에 큰 힘을 발휘한다.


Fetch API 심화 (Advanced Fetch API)

Fetch API 심화 (Advanced Fetch API)

자바스크립트에서 네트워크 요청을 다룰 때 Fetch API는 강력하고 유연한 도구다. 단순한 GET 요청부터 복잡한 설정까지, 다양한 상황에 맞춰 활용할 수 있다. 이번에는 Fetch API의 기본을 넘어 심화된 활용법을 코드와 함께 자세히 풀어보려고 한다.


Fetch API를 깊이 이해하면 요청 처리와 응답 관리를 훨씬 효율적으로 할 수 있다. 단계별로 차근차근 알아보자.


Fetch API 기본 복습

fetch는 Promise를 반환하며, 간단한 요청을 이렇게 처리할 수 있다:

fetch("https://jsonplaceholder.typicode.com/posts/1")
    .then((response) => response.json())
    .then((data) => console.log(data.title))
    .catch((error) => console.log(error));
// "sunt aut facere repellat provident occaecati excepturi optio reprehenderit"

async/await를 사용하면 더 깔끔해진다:

async function getPost() {
    const response = await fetch("https://jsonplaceholder.typicode.com/posts/1");
    const data = await response.json();
    console.log(data.title);
}

getPost();
// "sunt aut facere repellat provident occaecati excepturi optio reprehenderit"

이제 여기서 더 나아가 보자.


1. 요청 옵션 커스터마이징

fetch는 두 번째 인자로 옵션 객체를 받아 요청을 세밀하게 조정할 수 있다:

async function createPost() {
    const response = await fetch("https://jsonplaceholder.typicode.com/posts", {
        method: "POST",
        headers: {
            "Content-Type": "application/json"
        },
        body: JSON.stringify({
            title: "새 글",
            body: "내용입니다",
            userId: 1
        })
    });
    const data = await response.json();
    console.log(data);
}

createPost();
// { id: 101, title: "새 글", body: "내용입니다", userId: 1 }

POST 요청에 헤더와 본문을 추가해서 데이터를 전송했다.


2. 응답 상태 확인과 에러 처리

fetch는 404나 500 같은 오류 상태에서도 Promise를 거부하지 않으니, 상태를 직접 확인해야 한다:

async function fetchWithError(url) {
    const response = await fetch(url);
    if (!response.ok) {
        throw new Error(`HTTP 오류: ${response.status}`);
    }
    return response.json();
}

async function run() {
    try {
        const data = await fetchWithError("https://jsonplaceholder.typicode.com/posts/999");
        console.log(data);
    } catch (error) {
        console.log(error.message);
    }
}

run();
// "HTTP 오류: 404"

response.ok로 성공 여부를 확인하고, 실패 시 에러를 던졌다.


3. AbortController로 요청 중단

AbortController를 사용하면 요청을 중간에 취소할 수 있다:

async function fetchWithAbort() {
    const controller = new AbortController();
    const signal = controller.signal;

    setTimeout(() => controller.abort(), 1000);

    try {
        const response = await fetch(
            "https://jsonplaceholder.typicode.com/posts",
            { signal }
        );
        const data = await response.json();
        console.log(data);
    } catch (error) {
        if (error.name === "AbortError") {
            console.log("요청이 중단됨");
        } else {
            console.log(error.message);
        }
    }
}

fetchWithAbort();
// 1초 후 "요청이 중단됨"

1초 후 요청을 중단해서 불필요한 네트워크 작업을 막았다.


4. 스트림으로 응답 처리

Response.body를 사용하면 데이터를 스트림으로 받아올 수 있다:

async function streamResponse() {
    const response = await fetch("https://jsonplaceholder.typicode.com/posts");
    const reader = response.body.getReader();
    const decoder = new TextDecoder();
    let result = "";

    while (true) {
        const { done, value } = await reader.read();
        if (done) break;
        result += decoder.decode(value);
    }

    const data = JSON.parse(result);
    console.log(data[0].title);
}

streamResponse();
// "sunt aut facere repellat provident occaecati excepturi optio reprehenderit"

대용량 데이터를 조각 단위로 처리해서 메모리를 효율적으로 사용했다.


5. 파일 업로드 구현

FormData를 활용해서 파일을 업로드할 수 있다:

async function uploadFile() {
    const formData = new FormData();
    const file = new File(["테스트 데이터"], "test.txt", {
        type: "text/plain"
    });
    formData.append("file", file);
    formData.append("userId", "1");

    const response = await fetch("https://jsonplaceholder.typicode.com/posts", {
        method: "POST",
        body: formData
    });
    const data = await response.json();
    console.log(data);
}

uploadFile();
// { id: 101, ... } (서버 반영은 모의 데이터로 처리됨)

FormData로 파일과 추가 데이터를 함께 보냈다.


6. 캐싱과 요청 재사용

cache 옵션으로 캐싱 전략을 조정할 수 있다:

async function fetchWithCache() {
    const response = await fetch("https://jsonplaceholder.typicode.com/posts/1", {
        cache: "force-cache" // 캐시 강제 사용
    });
    const data = await response.json();
    console.log(data.title);
}

fetchWithCache();
// 캐시가 있으면 네트워크 요청 없이 캐시에서 가져옴

force-cache로 캐시를 우선 사용하도록 설정했다.


7. 요청 재시도 로직 추가

실패 시 재시도하는 로직을 구현해보자:

async function fetchWithRetry(url, retries = 3) {
    for (let i = 0; i < retries; i++) {
        try {
            const response = await fetch(url);
            if (!response.ok) {
                throw new Error(`HTTP 오류: ${response.status}`);
            }
            return response.json();
        } catch (error) {
            if (i === retries - 1) throw error;
            console.log(`재시도 ${i + 1} 실패: ${error.message}`);
            await new Promise((r) => setTimeout(r, 1000));
        }
    }
}

async function run() {
    try {
        const data = await fetchWithRetry("https://jsonplaceholder.typicode.com/posts/999");
        console.log(data);
    } catch (error) {
        console.log("최종 실패: " + error.message);
    }
}

run();
// "최종 실패: HTTP 오류: 404" (3번 재시도 후)

실패 시 1초 대기 후 최대 3번 재시도하도록 했다.


8. 성능과 안정성에 미치는 영향

Fetch API의 심화 활용이 코드에 어떤 영향을 주는지 살펴보자:

- 성능: 스트림 처리와 캐싱으로 대용량 데이터나 반복 요청을 최적화할 수 있다.

- 안정성: 에러 처리, 중단, 재시도로 네트워크 불안정성을 줄인다.

옵션 설정AbortController를 활용하면 요청 흐름을 완벽히 제어할 수 있다.


마무리

Fetch API는 단순한 요청을 넘어 스트림 처리, 파일 업로드, 재시도 등 다양한 상황에서 유연하게 동작한다. 옵션과 기능을 잘 조합하면 네트워크 작업을 안정적이고 효율적으로 관리할 수 있다.


히스토리 API (Browser History API)

히스토리 API (Browser History API)

브라우저에서 페이지 이동 없이 URL을 조작하고 기록을 관리하려면 History API가 큰 도움이 된다. SPA(싱글 페이지 애플리케이션)에서 자연스러운 네비게이션을 구현할 때 특히 유용하다. 이번에는 History API의 기본부터 심화 활용까지 코드와 함께 자세히 알아보려고 한다.


History API를 잘 활용하면 사용자 경험을 개선하고, 페이지 새로고침 없이도 깔끔한 상태 관리가 가능하다. 하나씩 차근차근 살펴보자.


History API 기본

window.history 객체를 통해 브라우저 기록을 다룰 수 있다. 가장 기본적인 메서드부터 보자:

console.log(window.history.length);
// 현재 세션의 기록 수 출력

history.back();
// 이전 페이지로 이동

history.forward();
// 다음 페이지로 이동

history.go(-2);
// 기록에서 두 단계 뒤로 이동

back, forward, go로 기록을 이동할 수 있다. 단순하지만 강력한 기능이다.


1. pushState로 기록 추가

pushState를 사용하면 페이지 새로고침 없이 URL을 변경하고 기록을 추가할 수 있다:

history.pushState(
    { page: "home" }, // 상태 객체
    "홈", // 제목 (현재는 무시됨)
    "/home" // URL
);

console.log(history.state);
// { page: "home" }

console.log(window.location.pathname);
// "/home"

URL이 /home으로 바뀌었지만 페이지는 새로고침되지 않았다. 상태 객체는 나중에 유용하게 쓰인다.


2. replaceState로 기록 수정

replaceState는 새 기록을 추가하지 않고 현재 기록을 수정한다:

history.pushState({ page: "about" }, "소개", "/about");
history.replaceState({ page: "about-updated" }, "소개 수정", "/about-us");

console.log(history.state);
// { page: "about-updated" }

console.log(location.pathname);
// "/about-us"

pushState로 기록이 추가된 후, replaceState로 현재 상태와 URL을 업데이트했다.


3. popstate 이벤트로 상태 감지

사용자가 뒤로 가기나 앞으로 가기를 누르면 popstate 이벤트가 발생한다:

window.addEventListener("popstate", (event) => {
    if (event.state) {
        console.log("상태: ", event.state);
    } else {
        console.log("상태 없음");
    }
});

history.pushState({ page: "profile" }, "프로필", "/profile");
history.pushState({ page: "settings" }, "설정", "/settings");
history.back();
// "상태: { page: 'profile' }"

뒤로 가기를 눌렀을 때 이전 상태를 감지해서 로그로 출력했다.


4. 간단한 SPA 구현

History API로 간단한 SPA 네비게이션을 만들어보자:

const content = document.createElement("div");
document.body.appendChild(content);

function renderPage(state) {
    const page = state ? state.page : "home";
    content.innerHTML = `현재 페이지: ${page}`;
}

window.addEventListener("popstate", (event) => {
    renderPage(event.state);
});

const links = [
    { path: "/home", state: { page: "home" } },
    { path: "/about", state: { page: "about" } },
    { path: "/contact", state: { page: "contact" } }
];

links.forEach((link) => {
    const a = document.createElement("a");
    a.href = link.path;
    a.textContent = link.state.page;
    a.addEventListener("click", (e) => {
        e.preventDefault();
        history.pushState(link.state, "", link.path);
        renderPage(link.state);
    });
    document.body.appendChild(a);
    document.body.appendChild(document.createElement("br"));
});

renderPage(history.state);

링크를 클릭하면 URL이 바뀌고 콘텐츠가 업데이트된다. 뒤로 가기와 앞으로 가기도 자연스럽게 동작한다.


5. 쿼리 파라미터와 조합

URL에 쿼리 파라미터를 추가해서 상태를 더 풍성하게 관리할 수 있다:

function renderContent(state, search) {
    const params = new URLSearchParams(search);
    const id = params.get("id") || "없음";
    console.log(`페이지: ${state.page}, ID: ${id}`);
}

window.addEventListener("popstate", (event) => {
    renderContent(event.state, location.search);
});

history.pushState(
    { page: "product" },
    "",
    "/product?id=123"
);
// "페이지: product, ID: 123"

history.pushState(
    { page: "product" },
    "",
    "/product?id=456"
);
// "페이지: product, ID: 456"

history.back();
// "페이지: product, ID: 123"

쿼리 파라미터를 활용해서 상태에 추가 정보를 담았다.


6. 동적 라우팅 처리

경로에 동적인 값을 넣어서 라우팅처럼 사용할 수 있다:

function getPageFromPath(pathname) {
    const match = pathname.match(/\/user\/(\d+)/);
    if (match) {
        return { page: "user", id: match[1] };
    }
    return { page: "home" };
}

function render(state) {
    console.log(`페이지: ${state.page}, ID: ${state.id || '없음'}`);
}

window.addEventListener("popstate", (event) => {
    const state = event.state || getPageFromPath(location.pathname);
    render(state);
});

history.pushState(
    { page: "user", id: "100" },
    "",
    "/user/100"
);
// "페이지: user, ID: 100"

history.pushState(
    { page: "user", id: "200" },
    "",
    "/user/200"
);
// "페이지: user, ID: 200"

history.back();
// "페이지: user, ID: 100"

정규식을 사용해서 동적 경로를 파싱하고 상태를 생성했다.


7. 상태 복원과 초기화

페이지를 새로고침했을 때도 상태를 복원할 수 있다:

function initPage() {
    const state = history.state || getPageFromPath(location.pathname);
    render(state);
}

window.addEventListener("popstate", (event) => {
    render(event.state);
});

history.pushState({ page: "settings" }, "", "/settings");
initPage();
// "페이지: settings, ID: 없음"

새로고침 후에도 history.state나 경로를 통해 상태를 복원했다.


8. 성능과 사용자 경험에 미치는 영향

History API가 코드와 사용자 경험에 어떤 영향을 주는지 보자:

- 성능: 페이지 새로고침 없이 상태를 변경하니 로드가 빠르다.

- 사용자 경험: 자연스러운 네비게이션과 뒤로 가기 지원으로 편리함이 커진다.

pushStatepopstate를 조화롭게 사용하면 SPA의 핵심 흐름을 만들 수 있다.


마무리

History API는 URL과 브라우저 기록을 유연하게 다룰 수 있게 해준다. 기본 메서드부터 동적 라우팅, 상태 복원까지 활용하면 SPA를 비롯한 다양한 환경에서 강력한 도구가 된다.


+ Recent posts