PHP의 개념과 특징

  • PHP (Hypertext Preprocessor)는 서버 측에서 실행되는 스크립트 언어로, 주로 웹 개발에 사용됩니다. 동적 웹 페이지를 생성하거나 데이터베이스와 상호작용하는 데 적합합니다.
  • 특징
    - 서버 측 실행: 클라이언트가 아닌 서버에서 처리되어 보안성이 높음
    - 플랫폼 독립적: Windows, Linux, macOS 등 다양한 운영 체제에서 실행 가능
    - 오픈 소스: 무료로 사용 가능하며 커뮤니티 지원이 풍부
    - HTML 내장 가능: HTML 코드 안에 PHP 코드를 삽입해 동적 콘텐츠 생성
    - 다양한 데이터베이스 지원: MySQL, PostgreSQL, SQLite 등과 쉽게 연동
    - 간단한 학습 곡선: 초보자도 쉽게 접근 가능

 

PHP 기본 문법과 변수

  • 기본 문법
    - PHP 코드는 <?php로 시작하고 ?>로 끝남 (종료 태그는 생략 가능)
    - 문장은 세미콜론(;)으로 끝남
    - 주석: 한 줄 주석(// 또는 #), 여러 줄 주석(/* */)
  • 변수
    - 변수는 $로 시작하며, 선언 시 데이터 타입을 명시하지 않음 (동적 타이핑)
    - 예: $name = "홍길동"; $age = 30
    - 변수 이름은 대소문자 구분하며, 숫자로 시작할 수 없음
  • 스코프
    - 지역 변수: 함수 내에서만 유효
    - 전역 변수: global 키워드 또는 $GLOBALS 배열로 접근
    - 정적 변수: static 키워드로 함수 호출 간 값 유지
  • 예제
<?php
$name = "홍길동";
$age = 30;
echo "이름: $name, 나이: $age";
?>

 

 

함수의 정의와 사용

  • 함수 정의
    - function 키워드로 정의하며, 매개변수와 반환값은 선택적
  • 기본 문법
function 함수명(매개변수) {
    // 코드
    return 값; // 선택적
}
  • 예제
<?php
function add($a, $b) {
    return $a + $b;
}

$result = add(3, 5);
echo $result; // 출력: 8
?>
  • 특징
    - 기본값 매개변수: function greet($name = "Guest") { ... }
    - 가변 인자: ...$args를 사용해 가변 개수의 인자 처리
    - 익명 함수: Closure로 사용 가능
$sayHello = function($name) {
    return "Hello, $name!";
};
echo $sayHello("홍길동"); // 출력: Hello, 홍길동!

 

 

폼 데이터 처리

  • PHP는 HTML 폼 데이터를 $_GET, $_POST, $_REQUEST 배열로 처리
    - GET: URL 쿼리 문자열로 데이터 전송 (보안 낮음, 데이터 크기 제한)
    - POST: HTTP 바디로 데이터 전송 (보안 높음, 대용량 가능)
  • 예제
<!-- form.html -->
<form action="process.php" method="post">
    이름: <input type="text" name="name">
    나이: <input type="number" name="age">
    <input type="submit" value="제출">
</form>
<!-- process.php -->
<?php
if ($_SERVER["REQUEST_METHOD"] == "POST") {
    $name = $_POST["name"];
    $age = $_POST["age"];
    echo "이름: $name, 나이: $age";
}
?>

 

 

데이터베이스와의 연동

  • PHP는 PDO 또는 mysqli 확장을 사용해 데이터베이스와 연동
  • PDO 예제 (MySQL 연동)
<?php
// PHP 코드의 시작을 나타내는 태그

$host = "localhost";
// 데이터베이스 서버의 호스트 주소를 설정 (여기서는 로컬 서버를 의미)

$dbname = "mydb";
// 연결하려는 데이터베이스의 이름을 지정

$username = "root";
// 데이터베이스에 접근하기 위한 사용자 이름 (기본적으로 MySQL의 루트 사용자)

$password = "";
// 데이터베이스 사용자 계정의 비밀번호 (여기서는 비밀번호 없음)

try {
// 예외 처리를 시작하여 데이터베이스 관련 오류를 캐치하기 위한 블록

    $pdo = new PDO("mysql:host=$host;dbname=$dbname", $username, $password);
    // PDO 객체를 생성하여 MySQL 데이터베이스에 연결 (DSN 문자열로 호스트와 데이터베이스 이름 지정)

    $pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
    // PDO의 오류 모드를 예외 모드로 설정하여 오류 발생 시 예외를 던지도록 함

    // 데이터 조회
    $stmt = $pdo->query("SELECT * FROM users");
    // users 테이블에서 모든 데이터를 조회하는 SQL 쿼리를 실행하고 결과를 $stmt에 저장

    while ($row = $stmt->fetch(PDO::FETCH_ASSOC)) {
    // 조회된 결과를 한 행씩 연관 배열(PDO::FETCH_ASSOC) 형태로 가져와 반복

        echo "ID: {$row['id']}, 이름: {$row['name']}<br>";
        // 각 행의 id와 name 값을 출력하고 줄바꿈(<br>) 추가

    }

    // 데이터 삽입
    $stmt = $pdo->prepare("INSERT INTO users (name, age) VALUES (:name, :age)");
    // users 테이블에 name과 age 값을 삽입하는 준비된 문장(prepared statement)을 생성

    $stmt->execute(['name' => '홍길동', 'age' => 30]);
    // 준비된 문장에 name과 age 값을 바인딩하여 실행 (홍길동, 30 삽입)

} catch (PDOException $e) {
// PDO 관련 예외가 발생하면 이를 캐치하여 처리

    echo "에러: " . $e->getMessage();
    // 예외 메시지를 출력하여 어떤 오류가 발생했는지 표시

}
?>
// PHP 코드의 종료를 나타내는 태그 (생략 가능)

 

  • mysqli 예제
<?php
// PHP 코드의 시작을 나타내는 태그

$conn = new mysqli("localhost", "root", "", "mydb");
// mysqli 객체를 생성하여 MySQL 데이터베이스에 연결 (호스트: localhost, 사용자: root, 비밀번호: 없음, 데이터베이스: mydb)

if ($conn->connect_error) {
// 데이터베이스 연결이 실패했는지 확인 (connect_error 속성에 오류 메시지가 있으면 실패)

    die("연결 실패: " . $conn->connect_error);
    // 연결 실패 시 오류 메시지를 출력하고 스크립트 실행을 종료

}

// 데이터 조회
$result = $conn->query("SELECT * FROM users");
// users 테이블에서 모든 데이터를 조회하는 SQL 쿼리를 실행하고 결과를 $result에 저장

while ($row = $result->fetch_assoc()) {
// 조회된 결과를 한 행씩 연관 배열 형태로 가져와 반복

    echo "ID: {$row['id']}, 이름: {$row['name']}<br>";
    // 각 행의 id와 name 값을 출력하고 줄바꿈(<br>) 추가

}

$conn->close();
// 데이터베이스 연결을 종료하여 리소스를 해제

?>
// PHP 코드의 종료를 나타내는 태그 (생략 가능)

 

 


다음과 같은 내용에 도전해봅시다.

 

1. php 설치 후 apache, MYSQL과 연결해보기

sudo apt install php libapache2-mod-php php-mysql -y

php -v

 

1-1. PHP와 Apache, MySQL 연동 테스트

테스트 PHP 파일 생성

sudo nano /var/www/html/test.php

 

PHP 코드 입력

<?php
phpinfo();
?>

 

브라우저에서 ip/test.php 접속하여 PHP 정보 페이지 확인

 

 

1-2. MySQL 연동 테스트

1-2-1. MySQL 데이터베이스 및 테이블 생성

sudo mysql -u root -p
CREATE DATABASE mydb;
USE mydb;
CREATE TABLE users (id INT AUTO_INCREMENT PRIMARY KEY, name VARCHAR(100), age INT);
INSERT INTO users (name, age) VALUES ('홍길동', 30);

 

1-2-2. PHP로 MySQL 연결 테스트

sudo nano /var/www/html/root_testdb.php

 

아래 php 코드 입력

<?php
ini_set('display_errors', 1);
ini_set('display_startup_errors', 1);
error_reporting(E_ALL);

$conn = new mysqli("172.31.85.190", "root", "your_password", "mydb");

if ($conn->connect_error) {
    die("연결 실패: " . $conn->connect_error);
}

echo "MySQL 연결 성공!<br>";
$conn->close();
?>

 

실행 결과

 

위 사진처럼 error의 화면을 볼 수 있는데, 이러한 오류가 뜨는 이유는 root 계정이 localhost에서만 접속 가능하도록 설정되어 있어 오류가 발생합니다.

 

1-2-3. PHP로 MySQL 연결 테스트(다른 계정 생성하여 사용)

MySQL 접속

sudo mysql -u root -p

 

모든 호스트에서 접속 가능한 사용자 생성
CREATE USER 'testuser'@'%' IDENTIFIED WITH mysql_native_password BY 'testpassword';
GRANT ALL PRIVILEGES ON mydb.* TO 'testuser'@'%';
FLUSH PRIVILEGES;

 

사용자 확인

 - host가 %로 출력되어야 함

SELECT user, host FROM mysql.user WHERE user = 'testuser';

 
MySQL 네트워크 설정 수정
sudo nano /etc/mysql/mysql.conf.d/mysqld.cnf

 

bind-address 설정 변경

[mysqld]
bind-address = 0.0.0.0

 

Mysql 재시작

sudo systemctl restart mysql

 

 

MYSQL 접속(testuser 사용자)

mysql -h 172.31.85.190 -u testuser -p

 

데이터베이스 및 테이블 생성

CREATE DATABASE IF NOT EXISTS mydb;
USE mydb;
CREATE TABLE IF NOT EXISTS users (
    id INT AUTO_INCREMENT PRIMARY KEY,
    name VARCHAR(100),
    age INT
);
INSERT INTO users (name, age) VALUES ('김철수', 25), ('이영희', 30);

 

데이터 확인

SELECT * FROM users;

 

PHP 코드 작성

sudo nano /var/www/html/testdb.php

 

아래 PHP 코드 작성

<?php
ini_set('display_errors', 1);
ini_set('display_startup_errors', 1);
error_reporting(E_ALL);

$conn = new mysqli("172.31.85.190", "testuser", "testpassword", "mydb");

if ($conn->connect_error) {
    die("연결 실패: " . $conn->connect_error);
}

echo "MySQL 연결 성공!<br>";

$result = $conn->query("SELECT * FROM users");
if ($result === false) {
    die("쿼리 오류: " . $conn->error);
}

while ($row = $result->fetch_assoc()) {
    echo "ID: {$row['id']}, 이름: {$row['name']}, 나이: {$row['age']}<br>";
}

$conn->close();
?>

 

실행결과

 

 

2. GET과 POST의 차이점 이해하기

 - GET과 POST는 HTTP 프로토콜에서 데이터를 서버로 전송하는 두 가지 주요 메서드입니다.

항목 GET POST
데이터 전송 방식 URL 쿼리 문자열에 포함 (?key=value&key2=value2) HTTP 요청 바디에 포함
보안성 데이터가 URL에 노출되어 보안 낮음 데이터가 바디에 숨겨져 있어 보안성 높음
데이터 크기 브라우저/서버 제한으로 소량 데이터 (약 2KB~4KB) 대용량 데이터 전송 가능 (서버 설정에 따라 다름)
용도 데이터 조회 (검색, 페이지 이동 등) 데이터 생성/수정 (폼 제출, 파일 업로드 등)
캐싱 브라우저에 캐싱 가능 (북마크 가능) 캐싱되지 않음
멱등성 멱등 (동일 요청 반복 시 동일 결과) 비멱등 (동일 요청이 다른 결과 초래 가능)
예제 <form method="get" action="search.php"> <form method="post" action="submit.php">

 

- 예제 코드
1. GET

get_test.html 파일 생성

sudo nano get_test.html

 

아래 HTML 코드 입력

<form method="get" action="search.php">
    <input type="text" name="query">
    <input type="submit" value="검색">
</form>

 

search.php 파일 생성

sudo nano search.php

 

아래 php 코드 입력

<!-- search.php -->
<?php
$query = $_GET['query'];
echo "검색어: $query";
?>

 

실행 결과

 - URL: search.php?query=1234

 

2. POST

post_test.html 파일 생성

sudo nano post_test.html

 

아래 HTML 코드 입력

<form method="post" action="submit.php">
    <input type="text" name="username">
    <input type="submit" value="제출">
</form>

 

 

실행결과

 - URL에 나타나지 않음

 

3. 파일 업로드 방법 이해하기

- PHP에서 파일 업로드는 HTML 폼과 $_FILES 배열을 사용해 처리합니다

upload.html 파일 생성

sudo nano upload.html

 

아래 HTML 코드 작성

 - enctype="multipart/form-data": 파일 업로드를 위해 필수
 - name="userfile": 서버에서 참조할 파일 입력 이름

<form action="upload.php" method="post" enctype="multipart/form-data">
    <input type="file" name="userfile">
    <input type="submit" value="업로드">
</form>

 

upload.php 파일 생성

sudo nano upload.php

 

아래 php 코드 작성

<!-- upload.php -->

// PHP 코드의 시작을 나타내는 태그
<?php

// HTTP 요청 메서드가 POST인지, 그리고 'userfile'이라는 이름의 파일이 업로드되었는지 확인
if ($_SERVER['REQUEST_METHOD'] == 'POST' && isset($_FILES['userfile'])) {

    // 업로드된 파일 정보를 $_FILES 배열에서 가져와 $file 변수에 저장
    $file = $_FILES['userfile'];

    // 파일 정보
    // 업로드된 파일의 원본 이름을 $name 변수에 저장 (예: "example.jpg")
    $name = $file['name'];
    // 서버에 임시로 저장된 파일의 경로를 $tmp_name 변수에 저장 (예: "/tmp/php1234")
    $tmp_name = $file['tmp_name'];

    // 파일 업로드 중 발생한 오류 코드를 $error 변수에 저장 (0은 성공을 의미)
    $error = $file['error'];

    // 업로드된 파일의 크기를 바이트 단위로 $size 변수에 저장
    $size = $file['size'];

    // 업로드된 파일의 MIME 타입을 $type 변수에 저장 (예: "image/jpeg")
    $type = $file['type'];

    // 업로드 디렉토리
    // 파일을 저장할 서버 내 디렉토리 경로를 $upload_dir 변수에 설정 (예: "uploads/")
    $upload_dir = 'uploads/';

    // 업로드된 파일의 최종 저장 경로를 $upload_file 변수에 설정 (예: "uploads/example.jpg")
    $upload_file = $upload_dir . basename($name);

    // 오류 체크
    // 파일 업로드 오류가 없는지 확인 (UPLOAD_ERR_OK는 0, 즉 성공을 의미)
    if ($error == UPLOAD_ERR_OK) {

        // 파일 크기 제한 (예: 5MB)
        // 파일 크기가 5MB(5 * 1024 * 1024 바이트)를 초과하는지 확인
        if ($size > 5 * 1024 * 1024) {

            // 파일 크기가 제한을 초과하면 오류 메시지 출력
            echo "파일이 너무 큽니다.";
            
        // 임시 파일($tmp_name)을 지정한 경로($upload_file)로 이동 시도
        } elseif (move_uploaded_file($tmp_name, $upload_file)) {

            // 파일 이동이 성공하면 성공 메시지와 파일 이름을 출력
            echo "파일 업로드 성공: $name";

        // 파일 이동이 실패한 경우
        } else {

            // 파일 업로드 실패 메시지 출력
            echo "파일 업로드 실패.";


        }
    // 파일 업로드 중 오류가 발생한 경우
    } else {

        // 오류 코드와 함께 업로드 오류 메시지 출력 (예: "업로드 오류: 4"는 파일이 업로드되지 않음을 의미)
        echo "업로드 오류: " . $error;


    }
// 조건문 종료
}

// PHP 코드의 종료를 나타내는 태그 (생략 가능)
?>

 

업로드 디렉토리 설정

sudo mkdir /var/www/html/uploads
sudo chmod 777 /var/www/html/uploads

 

실행결과

 

MySQL의 개념과 특징

  • 개념: MySQL은 오픈소스 관계형 데이터베이스 관리 시스템(RDBMS)으로, 데이터를 테이블 형태로 저장하고 SQL(Structured Query Language)을 사용해 관리합니다. 다양한 애플리케이션에서 데이터 저장 및 조회를 위해 널리 사용됩니다.
  • 특징
    - 오픈소스: 무료로 사용 가능하며, 커뮤니티와 상용 버전 제공
    - 고성능: 빠른 쿼리 처리와 대규모 데이터 처리에 적합
    - 확장성: 작은 애플리케이션부터 대규모 시스템까지 지원
    - 호환성: 다양한 운영체제(Windows, Linux, macOS) 및 프로그래밍 언어(PHP, Python, Java 등)와 호환
    - 보안: 사용자 인증, 암호화, 접근 제어 등 강력한 보안 기능 제공

 

데이터베이스 및 테이블 생성 방법

데이터베이스 생성

CREATE DATABASE example;

USE example;

CREATE TABLE users (
    id INT AUTO_INCREMENT PRIMARY KEY,
    name VARCHAR(50) NOT NULL,
    email VARCHAR(100) UNIQUE,
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);

 

 

기본적인 SQL 쿼리문 작성 방법

1. 데이터 조회(SELECT)

SELECT column1, column2 FROM table_name WHERE condition;

 

 - ex) 모든 사용자 조회

SELECT * FROM users;

 

- ex) 이름이 "Hong"인 사용자 조회

SELECT name, email FROM users WHERE name = 'Hong';

 

2. 데이터 정렬(ORDER BY)

 - ASC: 오름차순

 - DESC: 내림차순

SELECT * FROM users ORDER BY created_at DESC;

 

3. 데이터 그룹화(GROUP BY)

- ex) 이름별 사용자 수 집계

SELECT name, COUNT(*) FROM users GROUP BY name;

 

4. 테이블 조인(JOIN)

 - INNER JOIN: 두 테이블의 공통 데이터만 반환

 - LEFT JOIN: 왼쪽 테이블의 모든 데이터와 오른쪽 테이블의 일치 데이터 반환

 - RIGHT JOIN: 오른쪽 테이블의 모든 데이터와 왼쪽 테이블의 일치 데이터 반환

SELECT users.name, orders.order_date
FROM users
INNER JOIN orders ON users.id = orders.user_id;

 

5. 집계 함수

 -  COUNT(), SUM(), AVG(), MAX(), MIN()

SELECT AVG(age) FROM users;

 

 

데이터 조작 및 관리

1. 데이터 삽입(INSERT)

INSERT INTO table_name (column1, column2) VALUES (value1, value2);

 

- ex) 사용자 추가

INSERT INTO users (name, email) VALUES ('HonGiDong', 'HonGiDong@example.com');

 

- 여러 행 삽입

INSERT INTO users (name, email) VALUES 
    ('Bob', 'bob@example.com'),
    ('Charlie', 'charlie@example.com');

 

2. 데이터 수정(UPDATE)

UPDATE table_name SET column1 = value1 WHERE condition;

 

- ex) 이메일 업데이트

UPDATE users SET email = 'new_HongGiDong@example.com' WHERE name = 'HongGiDong';

 

3. 데이터 삭제(DELETE)

DELETE FROM table_name WHERE condition;

 

- ex) 특정 사용자 삭제

DELETE FROM users WHERE id = 1;

 

- 전체 데이터 삭제

DELETE FROM users;

 

4. 테이블 관리

 - 테이블 구조 변경

ALTER TABLE users ADD age INT;

 

- 테이블 삭제

DROP TABLE users;

 

5. 트랜잭션 관리

 - 트랜잭션 시작

START TRANSACTION;

 

- 변경 사항 저장

COMMIT;

 

- 변경 사항 취소

ROLLBACK;

 

- ex) 사용 예시

  이 코드는 users 테이블에 'PuRin'라는 사용자를 추가한 뒤, 그의 이메일을 즉시 업데이트하는 작업을 트랜잭션으로 안전하게 처리하며, 만약 중간에 오류가 발생하면 COMMIT이 실행되지 않고, 변경 사항은 적용되지 않을 수 있습니다.(롤백 가능)

START TRANSACTION;
INSERT INTO users (name, email) VALUES ('PuRin', 'PuRin@example.com');
UPDATE users SET email = 'PuRin_new@example.com' WHERE name = 'PuRin';
COMMIT;

 

6. 인덱스 생성

 - 조회 성능 향상

CREATE INDEX idx_name ON users(name);

 

7. 백업 및 복구

 - 데이터베이스 백업(mysqldump 사용)

mysqldump -u username -p database_name > backup.sql

 

- 복구

mysql -u username -p database_name < backup.sql

 


다음과 같이 내용에 도전해봅시다.

1. Mysql 로컬 환경에서 설치 후 실습 하기

  • 로컬환경으로 사용되는 WSL로 실습 진행

 

1-1. Mysql 설치하기

sudo apt update && sudo apt upgrade -y
sudo apt-get install mysql-server

 

1-2. MySQL 서비스 시작

sudo systemctl start mysql

 

1-3. 설치 확인

 - 초기 비밀번호는 아무 입력 없이 엔터 치면 mysql 실행 가능 합니다.

mysql -u root -p

 

1-4. MySQL의 기본 구조와 데이터베이스 생성 실습하기

CREATE DATABASE example;

USE example;

CREATE TABLE users (
    id INT AUTO_INCREMENT PRIMARY KEY,
    username VARCHAR(50) NOT NULL,
    email VARCHAR(100) NOT NULL
);

 

1-5. 데이터 조작 및 관리를 위한 기본적인 SQL 쿼리문

INSERT INTO users (username, email) VALUES ('user1', 'user1@example.com');
SELECT * FROM users;
UPDATE users SET email = 'newemail@example.com' WHERE id = 1;
DELETE FROM users WHERE id = 1;

 

1-6. 데이터베이스 관리

CREATE USER 'newuser'@'localhost' IDENTIFIED BY 'password';
GRANT ALL PRIVILEGES ON example_db.* TO 'newuser'@'localhost';

 

mysqldump -u username -p example_db > example_db_backup.sql
mysql -u username -p example_db < example_db_backup.sql

 

 

 

2.JOIN을 사용하여 여러 테이블 간 관계 설정하기

테스트 전 DB 세팅

 

테이블 생성

 - customers: 고객 정보 저장
 - orders: 고객별 주문 정보 저장, customer_id로 customers와 연결
 - order_details: 주문별 상품 상세 정보, order_id로 orders와 연결
 - FOREIGN KEY: 테이블 간 관계를 명시적으로 정의해 데이터 무결성 보장

CREATE DATABASE join_test_db;
USE join_test_db;

CREATE TABLE customers (
    customer_id INT AUTO_INCREMENT PRIMARY KEY,
    name VARCHAR(50) NOT NULL,
    email VARCHAR(100) UNIQUE
);

CREATE TABLE orders (
    order_id INT AUTO_INCREMENT PRIMARY KEY,
    customer_id INT,
    order_date DATE,
    total_amount DECIMAL(10, 2),
    FOREIGN KEY (customer_id) REFERENCES customers(customer_id)
);

CREATE TABLE order_details (
    detail_id INT AUTO_INCREMENT PRIMARY KEY,
    order_id INT,
    product_name VARCHAR(50),
    quantity INT,
    unit_price DECIMAL(10, 2),
    FOREIGN KEY (order_id) REFERENCES orders(order_id)
);

 

데이터 삽입

 - customers: 3명 추가 (Alice, Bob, Charlie)
 - orders: Alice(2개), Bob(1개) 주문 추가. Charlie는 주문 없음
 - order_details: 각 주문에 대한 상품 상세 추가

INSERT INTO customers (name, email) VALUES
    ('Alice', 'alice@example.com'),
    ('Bob', 'bob@example.com'),
    ('Charlie', 'charlie@example.com');

INSERT INTO orders (customer_id, order_date, total_amount) VALUES
    (1, '2025-04-01', 150.50),  -- Alice의 주문
    (1, '2025-04-02', 300.00),  -- Alice의 두 번째 주문
    (2, '2025-04-03', 99.99);   -- Bob의 주문
    -- Charlie는 주문 없음

INSERT INTO order_details (order_id, product_name, quantity, unit_price) VALUES
    (1, 'Laptop', 1, 100.00),
    (1, 'Mouse', 2, 25.25),
    (2, 'Phone', 1, 300.00),
    (3, 'Headphones', 1, 99.99);

 

 

데이터 확인

SELECT * FROM customers;
SELECT * FROM orders;
SELECT * FROM order_details;

 

2-1. INNER JOIN

 설명: 두 테이블에서 조건에 맞는 데이터만 반환

SELECT c.name, c.email, o.order_id, o.order_date, o.total_amount
FROM customers c
INNER JOIN orders o ON c.customer_id = o.customer_id;

 

 결과: Alice와 Bob의 주문만 표시 (Charlie는 주문이 없으므로 제외)

 

2-2. LEFT JOUN

 설명: 왼쪽 테이블(customers)의 모든 데이터와 오른쪽 테이블(orders)의 일치하는 데이터 반환. 일치하지 않으면 NULL

SELECT c.name, c.email, o.order_id, o.order_date
FROM customers c
LEFT JOIN orders o ON c.customer_id = o.customer_id;

 

 결과: Charlie도 포함되며, 주문 정보는 NULL

 

 

2-3. RIGHT JOIN

 설명: 오른쪽 테이블(orders)의 모든 데이터와 왼쪽 테이블(customers)의 일치하는 데이터 반환

SELECT c.name, c.email, o.order_id, o.order_date
FROM customers c
RIGHT JOIN orders o ON c.customer_id = o.customer_id;

 

 결과 : 모든 주문과 관련 고객 정보 표시. Charlie는 주문이 없으므로 제외

 

 

2-4.다중 테이블 JOIN

 설명: customers, orders, order_details를 모두 연결

SELECT c.name, o.order_id, o.order_date, od.product_name, od.quantity, od.unit_price
FROM customers c
INNER JOIN orders o ON c.customer_id = o.customer_id
INNER JOIN order_details od ON o.order_id = od.order_id;

 

결과: 고객, 주문, 주문 상세 정보를 함께 표시

 

2-5. LEFT JOIN으로 상세 정보 포함

 설명: 주문이 없는 고객도 포함하여, 주문 상세까지 조회

SELECT c.name, o.order_id, o.order_date, od.product_name
FROM customers c
LEFT JOIN orders o ON c.customer_id = o.customer_id
LEFT JOIN order_details od ON o.order_id = od.order_id;

 

 결과: Charlie는 주문이 없으므로 order_id, product_name 등이 NULL

 

 

3. 데이터베이스 접근 제한, 사용자 권한 설정하기

테스트 전 DB 세팅

 

테이블 생성

CREATE DATABASE permission_test_db;
USE permission_test_db;

-- 부서 테이블
CREATE TABLE departments (
    dept_id INT AUTO_INCREMENT PRIMARY KEY,
    dept_name VARCHAR(50) NOT NULL
);

-- 직원 테이블
CREATE TABLE employees (
    emp_id INT AUTO_INCREMENT PRIMARY KEY,
    name VARCHAR(50) NOT NULL,
    dept_id INT,
    salary DECIMAL(10, 2),
    FOREIGN KEY (dept_id) REFERENCES departments(dept_id)
);

 

테스트 데이터 삽입

INSERT INTO departments (dept_name) VALUES
    ('HR'),
    ('IT'),
    ('Sales');

INSERT INTO employees (name, dept_id, salary) VALUES
    ('Alice', 1, 50000.00),
    ('Bob', 2, 60000.00),
    ('Charlie', 3, 55000.00);

 

데이터 확인

SELECT * FROM departments;
SELECT * FROM employees;

 

3-1. 사용자 생성

 - 'localhost' : 로컬 접속 제한

 - '%': 모든 호스트에서 접속 허용

 - 설명

    read_only_user: 읽기 전용 권한 테스트
    write_user: 쓰기 및 수정 권한 테스트
    admin_user: 모든 권한 테스트

CREATE USER 'read_only_user'@'localhost' IDENTIFIED BY 'readpass123';
CREATE USER 'write_user'@'localhost' IDENTIFIED BY 'writepass123';
CREATE USER 'admin_user'@'localhost' IDENTIFIED BY 'adminpass123';

 

3-2. 권한 부여

읽기 전용 사용자

 - employeesdepartmentsSELECT 권한 부여

GRANT SELECT ON permission_test_db.employees TO 'read_only_user'@'localhost';
GRANT SELECT ON permission_test_db.departments TO 'read_only_user'@'localhost';

 

쓰기 사용자

 - employeesSELECT, INSERT, UPDATE 권한 부여

GRANT SELECT, INSERT, UPDATE ON permission_test_db.employees TO 'write_user'@'localhost';

 

- departments에는 SELECT만 부여

GRANT SELECT ON permission_test_db.departments TO 'write_user'@'localhost';

 

관리자 사용자

 - permission_test_db의 모든 권한 부여

GRANT ALL PRIVILEGES ON permission_test_db.* TO 'admin_user'@'localhost';

 

3-3. 권한 적용

 - 변경사항 즉시 적용

FLUSH PRIVILEGES;

 

3-4. 권한 확인

 - 각 사용자의 권한 확인

SHOW GRANTS FOR 'read_only_user'@'localhost';
SHOW GRANTS FOR 'write_user'@'localhost';
SHOW GRANTS FOR 'admin_user'@'localhost';

 

3-5. 권한 테스트

3-5-1. 읽기 전용 사용자 테스트(read_only_user)

접속

mysql -u read_only_user -p

USE permission_test_db;

 

쿼리 테스트

 - 데이터 조회(성공)

SELECT * FROM employees;
SELECT * FROM departments;

 

 - 데이터 삽입(실패)

INSERT INTO employees (name, dept_id, salary) VALUES ('Dave', 1, 52000.00);

 

- 데이터 수정(실패)

UPDATE employees SET salary = 60000.00 WHERE emp_id = 1;

 

3-5-2.  쓰기 사용자 테스트(write_user)

mysql -u write_user -p

USE permission_test_db;

 

 - employees 조회(성공)

SELECT * FROM employees;

 

- employees 데이터 삽입 (성공)

INSERT INTO employees (name, dept_id, salary) VALUES ('Dave', 2, 62000.00);
SELECT * FROM employees; -- Dave 추가 확인

 

- departments 삽입(실패)

INSERT INTO departments (dept_name) VALUES ('Marketing');

 

- employees 삭제(실패)

DELETE FROM employees WHERE emp_id = 1;

 

3-5-3. 관리자 사용자 테스트(admin_user)

mysql -u admin_user -p

USE permission_test_db;

 

- employees 조회(성공)

SELECT * FROM employees;

 

- departments 삽입 (성공)

INSERT INTO departments (dept_name) VALUES ('Marketing');

 

- employees 업데이트 (성공)

UPDATE employees SET salary = 70000.00 WHERE emp_id = 1;

 

 

- employees 삭제 (성공)

DELETE FROM employees WHERE name = 'Charlie';

아파치 웹 서버의 개념

  • 아파치(Apache HTTP Server)는 오픈소스 웹 서버 소프트웨어로, HTTP 요청을 처리하여 웹 콘텐츠를 클라이언트에 제공합니다.
  • 전 세계적으로 가장 널리 사용되는 웹 서버 중 하나로, 유연성, 확장성, 안정성을 특징으로 합니다. 다양한 운영 체제(리눅스, 윈도우 등)에서 동작하며, 정적/동적 콘텐츠를 처리할 수 있습니다.

 

아파치의 기본 구조와 모듈

  • 기본 구조
     - 코어: 아파치의 핵심 기능(HTTP 요청 처리, 설정 파일 파싱 등)을 담당
     - 모듈: 특정 기능을 추가하는 플러그인 형태로, 동적으로 로드/제거 가능
     - MPM(Multi-Processing Module): 요청 처리 방식 결정(예: prefork, worker, event)
     - 핸들러: 요청에 대한 처리 방식 정의(예: CGI, PHP)
  • 주요 모듈:
     - mod_rewrite: URL 재작성
     - mod_ssl: HTTPS 지원
     - mod_proxy: 프록시 기능
     - mod_auth*: 인증 및 권한 관리
     - mod_cache: 캐싱으로 성능 최적화

 

아파치의 주요 설정 파일

  • httpd.conf: 아파치의 주 설정 파일. 서버 전반의 설정(포트, 디렉토리, 모듈 로드 등)을 정의
  • 경로: 보통 /etc/httpd/conf/ 또는 /etc/apache2/에 위치
  • apache2.conf: 데비안 계열 시스템에서 사용되는 주 설정 파일
  • conf.d/: 추가 설정 파일을 포함하는 디렉토리
  • sites-available/ 및 sites-enabled/: 가상 호스트 설정 관리(데비안 계열)
  • mods-available/ 및 mods-enabled/: 모듈 활성화/비활성화 관리
  • envvars: 환경 변수 설정 파일

 

.htaccess 파일의 사용법

  • .htaccess는 아파치 웹 서버에서 디렉토리별로 설정을 제어하는 파일로, 서버 전체 설정(httpd.conf)을 변경하지 않고 특정 디렉토리의 동작을 커스터마이징할 수 있습니다.
  • .htaccess 파일 작성 시 주의사항
    1. 문법 오류: 잘못된 설정은 500 Internal Server Error를 유발할 수 있음. 설정 전 테스트 권장
    2. 성능 고려: .htaccess는 요청마다 파일을 읽으므로 과도한 사용은 서버 성능 저하를 초래, 가능하면 설정을  httpd.conf에 통합
    3 .htaccess 자체를 보호: <Files ".htaccess"> Order Deny,Allow Deny from all </Files>.
    4. 민감한 정보(예: DB 비밀번호)는 포함시키지 말 것.
    5. 백업: 변경 전 원본 파일 백업.
    6. 모듈 확인: 사용하려는 기능(예: mod_rewrite, mod_auth)이 서버에 활성화되어 있는지 확인.
  • .htaccess 파일 위치
    - 보통 웹 루트 디렉토리(/var/www/html/) 또는 하위 디렉토리에 위치
    - 특정 디렉토리에만 영향을 미치며, 하위 디렉토리에도 상속됨(상위 설정이 우선)
  • 테스트 방법
    - 설정 후 브라우저로 확인
    - 오류 로그 확인: /var/log/apache2/error.log 또는 /var/log/httpd/error_log
    - 아파치 설정 테스트: apachectl configtest

 

1. htaccess 활성화

 - .htaccess를 사용하려면 주 설정 파일(httpd.conf 또는 apache2.conf)에서 해당 디렉토리에 대해 AllowOverride 지시어를 설정해야 합니다.

- AllowOverride All: 모든 .htaccess 설정 허용
  AllowOverride None: .htaccess 비활성화
  설정 변경 후 아파치 재시작: sudo systemctl restart apache2 또는 sudo service httpd restart

<Directory /var/www/html>
    AllowOverride All
    Require all granted
</Directory>

 

 

2. 주요 사용 사례와 설정 예시

2-1. URL 재작성(mode_rewrite)

 - URL을 동적으로 변경하거나 리다이렉트
 - ex) example.com/old-page를 example.com/new-page로 301 리다이렉트

    R=301: 영구 리다이렉트 / L: 이후 규칙 무시

RewriteEngine On
RewriteRule ^old-page$ new-page [R=301,L]

 

2-2. 접근 제어

- 특정 IP 또는 사용자에 대한 접근 제한
- ex) 특정 IP만 접근 허용

Order Deny,Allow
Deny from all
Allow from 192.168.1.0/24

 

- ex) 디렉토리 접근 차단

Deny from all

 

2-3. 비밀번호 보호(mod_auth)

- 디렉토리에 비밀번호 인증 추가
- .htpasswd 파일 생성: htpasswd -c /path/to/.htpasswd username

- 설정

AuthType Basic
AuthName "Restricted Area"
AuthUserFile /path/to/.htpasswd
Require valid-user

 

2-4. 커스텀 에러 페이지

 - 특정 HTTP 에러 코드에 대해 사용자 정의 페이지 표시
 - ex) 404, 403, 500 에러 페이지 설정

ErrorDocument 404 /custom_404.html
ErrorDocument 403 /custom_403.html
ErrorDocument 500 /custom_500.html

 

2-5. MIME 타입 및 캐싱 제어

 - 파일 타입별 캐싱 설정(mod_expires 사용)

<IfModule mod_expires.c>
    ExpiresActive On
    ExpiresByType image/jpeg "access plus 1 month"
    ExpiresByType text/css "access plus 1 week"
</IfModule>

 

2-6. 파일 확장자 숨기기 또는 강제 리다이렉트

 - ex) .php 확장자 숨기기

RewriteEngine On
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_FILENAME}\.php -f
RewriteRule ^(.*)$ $1.php [L]

 

2.7 디렉토리 목록 비활성화

- 디렉토리 내 파일 목록 표시 방지

Options -Indexes

 

2.8 Gzip 압축 활성화

 - 콘텐츠 압축으로 전송 속도 향상

<IfModule mod_deflate.c>
    AddOutputFilterByType DEFLATE text/html text/css application/javascript
</IfModule>

 

 

아파치 성능 튜닝의 기초

  • 아파치 웹 서버의 성능 튜닝은 서버 자원(CPU, 메모리, 네트워크)을 효율적으로 사용하고 응답 시간을 단축하며 동시 접속자를 효과적으로 처리하는 것을 목표로 합니다.

1. MPM(Multi-Processing Module) 최적화
 - MPM은 아파치가 요청을 처리하는 방식을 결정합니다. 사용 중인 MPM(prefork, worker, event)에 따라 설정을 조정합니다.

 

 - MPM 종류

   1. prefork: 프로세스 기반, 안정적이지만 메모리 사용량이 높음. PHP와 호환성이 좋음
   2.  worker: 스레드 기반, 메모리 효율적이며 동시 접속 처리에 유리
   3.  event: 비동기 처리로, KeepAlive 연결에서 성능 향상

 

- MPM 확인

apachectl -V | grep MPM

 

- 설정

<IfModule mpm_worker_module>
    ServerLimit         250      # 최대 프로세스 수
    StartServers        5        # 시작 시 프로세스 수
    MaxClients          200      # 최대 동시 접속자 수
    MinSpareThreads     25       # 최소 대기 스레드
    MaxSpareThreads     75       # 최대 대기 스레드
    ThreadsPerChild     25       # 프로세스당 스레드 수
    MaxRequestsPerChild 10000    # 프로세스 재사용 한도
</IfModule>

 

2. 불필요한 모듈 비활성화

 - 기본적으로 로드되는 모듈이 많아 자원을 낭비할 수 있음

 - 확인 명령어

apachectl -M

 

 - 비활성화 방법

   1. 데비안 계열: a2dismod <module_name>

   2. CentOS 계열: /etc/httpd/conf.modules.d/에서 모듈 로드 주석 처리

 

3. KeepAlive 설정

 - KeepAlive는 클라이언트와의 연결을 유지해 반복 요청 시 오버헤드를 줄입니다.
 - 설정 예시 (httpd.conf)

    1. KeepAliveTimeout: 2~5초로 설정해 연결 유지 시간 최소화
    2. 높은 트래픽 시 MaxKeepAliveRequests 낮추기

KeepAlive On
KeepAliveTimeout 5    # 연결 유지 시간(초)
MaxKeepAliveRequests 100  # 연결당 최대 요청 수

 

4. 캐싱 활성화

 - 정적 콘텐츠(이미지, CSS, JS 등)를 캐싱해 서버 부하 감소

 - mod_expires

<IfModule mod_expires.c>
    ExpiresActive On
    ExpiresByType image/jpeg "access plus 1 month"
    ExpiresByType text/css "access plus 1 week"
    ExpiresByType application/javascript "access plus 1 week"
</IfModule>

 

- mod_cache(디스크/메모리 캐싱)

<IfModule mod_cache.c>
    CacheEnable disk /
    CacheRoot "/var/cache/apache2"
</IfModule>

 

5. 로그 최적화

 - 불필요한 로그는 디스크 I/O와 성능에 영향을 미침

 - 설정(액서스 로그 최소화)

   LogLevel을 error 또는 warn 으로 설정

LogLevel warn
CustomLog /var/log/apache2/access.log combined

 

6. 연결 및 타임아웃 설정

 - 요청 처리 시간을 최적화해 대기 시간 감소

 - 설정

Timeout 30          # 전체 요청 타임아웃(초)
RequestReadTimeout header=20-40,MinRate=500 body=20,MinRate=500

 

7. 하드웨어 및 OS 최적화

 - 메모리: MaxClients를 서버 메모리에 맞게 설정
 - 파일 디스크립터: 최대 열린 파일 수 증가

ulimit -n 65535

 


다음과 같은 내용에 도전해봅시다.

 

1. 아파치와 Nginx의 차이점 이해하기

  아파치(Apache) Nginx
아키텍처 프로세스 기반 모델(MPM: Multi-Processing Module)을 사용. prefork는 요청당 프로세스 생성, worker는 스레드 기반으로 동작. 동적 콘텐츠 처리에 강력 비동기, 이벤트 기반 모델. 단일 스레드로 다수의 연결을 효율적으로 처리. 높은 동시 접속 처리에 최적
성능 모듈이 많아 유연하지만, 높은 동시 접속 시 메모리와 CPU 사용량 증가 정적 콘텐츠 제공과 동시 접속 처리에서 빠르고, 메모리 사용량이 적음. 역방향 프록시 및 로드 밸런싱에 강점
설정 httpd.conf와 .htaccess를 통한 설정. 모듈 기반으로 유연하지만 복잡할 수 있음 간단한 구성 파일(nginx.conf) 사용. 설정이 직관적이며, .htaccess 지원 없음
모듈 지원 다양한 모듈(mod_rewrite, mod_php 등)로 확장 가능. 동적 로딩 지원 모듈은 컴파일 시 포함해야 하며, 아파치보다 제한적. 하지만 기본 기능이 강력
사용 사례 동적 콘텐츠(예: PHP 기반 워드프레스)와 복잡한 설정이 필요한 환경에 적 정적 콘텐츠, 고트래픽 사이트, 프록시 서버, 로드 밸런서로 사용 시 탁월

 

 

2. Tomcat에 대해 이해하기

  • Apache Tomcat은 Java 기반 웹 애플리케이션을 실행하기 위한 오픈소스 서블릿 컨테이너이자 웹 서버입니다.
  • Java Servlet, JSP(JavaServer Pages), WebSocket 등을 지원하며, 주로 Java EE 애플리케이션 호스팅에 사용됩니다.
  • 주요 기능
    - 서블릿 컨테이너: Java Servlet과 JSP를 실행해 동적 웹 콘텐츠 생성
    - 웹 서버: HTTP 요청을 처리하며, 정적 콘텐츠도 제공 가능(최신 버전에서 개선)
    - 클러스터링: 다중 Tomcat 인스턴스로 세션 공유 및 부하 분산 지원
    - 보안: Realm을 통한 인증/인가, SSL/TLS 지원
  • 구성 요소
    - Catalina: 서블릿 컨테이너의 핵심 엔진
    - Coyote: HTTP, AJP 프로토콜 처리
    - Jasper: JSP를 Java 코드로 컴파일
  • 설정
    - 주요 설정 파일: server.xml (서버 포트, 커넥터 등), web.xml (애플리케이션 설정)
    - WAR 파일 배포: 웹 애플리케이션을 /webapps 디렉토리에 배포
  • 장점
    - Java 애플리케이션에 특화, 안정적이고 성숙한 플랫폼
    - 커뮤니티 지원 강력(Apache Software Foundation)
    - 다양한 OS 지원(리눅스, 윈도우 등)
  • 단점
    - 정적 콘텐츠 처리 속도가 Nginx/아파치보다 느림
     메모리 사용량이 상대적으로 높음(Java 기반)
    - 복잡한 설정과 Java 지식 요구

 

3. apache 로컬환경 or 클라우드서버 에서 설치 후 실습해보기

  • 로컬환경으로 사용되는 WSL로 실습 진행

1. 패키지 업데이트 진행

sudo apt update && sudo apt upgrade -y

 

2. 아파치 설치

sudo apt install apache2 -y

 

3. 아파치 서비스 시작 및 활성화

sudo systemctl start apache2
sudo systemctl enable apache2

 

4. 설치 확인

 - 브라우저에서 http://localhost (로컬) 또는 http://IP 접속
 - 기본 아파치 환영 페이지가 표시되면 성공

 

5. 상태 확인

 

6. 간단한 HTML 페이지 작성

웹 루트 디렉토리 확인

  - 기본 디렉토리: /var/www/html

 

테스트 전 index.html 백업 파일 만들기

sudo cp /var/www/html/index.html /var/www/html/index_backup.html

 

HTML 파일 생성

sudo nano /var/www/html/index.html

 

HTML 소스 입력 ( 이전에 사용한 자기소개 코드 사용)

<!DOCTYPE html>
<html>
<head>
    <title>나의 소개</title>
    <style>
        body {
            background-color: #f0f8ff;
            font-family: Arial, sans-serif;
            padding: 20px;
        }
        h1 {
            color: #3333cc;
        }
        p {
            color: #000000;
            font-size: 18px;
            line-height: 1.6;
        }
        button {
            margin-top: 20px;
            padding: 10px 20px;
            background-color: #4CAF50;
            color: white;
            border: none;
            border-radius: 8px;
            cursor: pointer;
        }
        button:hover {
            background-color: #45a049;
        }
    </style>
</head>
<body>

    <h1>안녕하세요! 👋</h1>
    
    <p>저는 보안관제로 근무중이며,모의해킹으로 이직을 하기 위해<br> <strong>ELITE HACKER Bootcamp 4th</strong>를 배우고 있는 직장인입니다.</p>
    <p>진행중인 <strong>pre.Web</strong>, <strong>pre.Rev</strong> 모두에 관심이 있어요.</p>
    <p><strong>pre.Web</strong> 코스를 다 끝내고 나면 <strong>pre.Rev</strong> 코스도 도전할 예정입니다!</p>
    <p>취미는 🎮 게임, 🎤 노래 부르기 입니다!</p>

    <button onclick="alert('응원합니다! 화이팅!')">응원 받기</button>

</body>
</html>

 

권한 설정

sudo chown -R www-data:www-data /var/www/html
sudo chmod -R 755 /var/www/html

 

확인 

 - 브라우저에서 http://localhost (로컬) 또는 http://IP 접속

 

위 사진 처럼 한글이 깨지는 현상을 확인할 수 있었고, 해결하기 위해 아파치 설정을 수정하였습니다.

 

charset.conf 로 접근하여 AddDefaultCharset UTF-8이 주석으로 처리되어 있는데 주석을 지우고 저장합니다.

sudo nano /etc/apache2/conf-available/charset.conf

 

 

설정을 적용하기 위해 아파치 재시작을 합니다.

sudo service apache2 restart

 

UTF-8 이 적용되어 한글이 제대로 나오는 것을 확인할 수 있었습니다.

 

7. .htaccess 실습

 - .htaccess를 사용해 간단한 URL 리다이렉트를 설정하고 Windows에서 테스트합니다.

 

7-1. mod_rewrite 활성화
sudo a2enmod rewrite

 

7.2. .htaceess 허용

 000-default.conf 접근

sudo nano /etc/apache2/sites-available/000-default.conf

 

<VirtualHost> 섹션 내에 추가

<Directory /var/www/html>
    Options Indexes FollowSymLinks
    AllowOverride All
    Require all granted
</Directory>

 

7.3. .htaccess 생성

sudo nano /var/www/html/.htaccess

 

아래 내용 입력 (/old_age를 /new_page.html로 리다이렉트)

 - RewriteBase / 를 제외하고 진행하였으나, 리다이렉트가 /var/www/html/new_page.html로 되었으며 정상적인 페이지가

   확인 되지 않았습니다.

RewriteEngine On
RewriteBase /
RewriteRule ^old_page$ new_page.html [R=301,L]

 

테스트용 페이지 생성

sudo nano /var/www/html/new-page.html

 

아래 내용 입력

<!DOCTYPE html>
<html>
<body>
    <h1>New Page</h1>
    <p>You have been redirected here!</p>
</body>
</html>

 

아파치 재시작

sudo systemctl restart apache2

 

 

실행 결과

 - old_page 를 입력하여 접속하였으나 리다이렉트되어 new_page.html 화면이 보이는 것을 확인하였습니다.

 

 

8. 비밀번호 보호 디렉토리 설정

 - 특정 디렉토리에 비밀번호 인증을 추가해 접근을 제한
 - htaccess와 .htpasswd를 사용한 보안 설정 학습

 

8-1. 보호할 디렉토리 생성

sudo mkdir /var/www/html/secret
sudo nano /var/www/html/secret/index.html

 

HTML 코드 입력

<!DOCTYPE html>
<html>
<body>
    <h1>Secret Area</h1>
    <p>This is a password-protected page!</p>
</body>
</html>

 

8-2. .htpasswd 파일 생성

 - 사용자 인증을 위한 비밀번호 파일 생성

sudo htpasswd -c /etc/apache2/.htpasswd testuser

 

8.3. .htaccess 설정

sudo nano /var/www/html/secret/.htaccess

 

아래 코드 입력

AuthType Basic
AuthName "Restricted Area"
AuthUserFile /etc/apache2/.htpasswd
Require valid-user

 

8-4. 권한 설정

sudo chown -R www-data:www-data /var/www/html/secret
sudo chmod -R 755 /var/www/html/secret

 

8-5. 아파치 설정 확인

 - /etc/apache2/sites-available/000-default.conf에서 AllowOverride All 확인

 - 위에서 실습 진행할때 적용 해두었습니다.

 

8-6. 아파치 재시작

sudo systemctl restart apache2

 

8-7. 테스트
Windows 브라우저에서 http://localhost/secret 접속


사용자 이름(testuser)과 비밀번호 입력

 

"Secret Area" 페이지 표시 확인

 

잘못된 비밀번호 입력 시 401 Unauthorized 확인

 

9. 커스텀 에러 페이지 설정

 - 404, 403 등의 HTTP 에러에 사용자 정의 페이지 제공

 

9-1.에러 페이지 생성

sudo nano /var/www/html/404.html

 

HTML 코드 입력

<!DOCTYPE html>
<html>
<body>
    <h1>404 - Page Not Found</h1>
    <p>Sorry, the page you requested does not exist.</p>
</body>
</html>

 

9-2. .htaccess 설정

sudo nano /var/www/html/.htaccess

 

아래 코드 추가

ErrorDocument 404 /404.html

 

9-3. 권한 설정

sudo chown www-data:www-data /var/www/html/404.html
sudo chmod 755 /var/www/html/404.html

 

9-4. 아파치 재시작

sudo systemctl restart apache2

 

9-5. 테스트

 - 브라우저에서 생성되어 있지 않은 파일로 접속
 - "404 - Page Not Found" 페이지 표시 확인

 

 

10. PHP 연동 및 동적 페이지 

 - 아파치에 PHP를 설치해 동적 웹 페이지 제공
 - 간단한 PHP 스크립트로 서버 정보 출력

 

10-1. PHP 설치

sudo apt update
sudo apt install php libapache2-mod-php -y

 

 

10-2.PHP 테스트 파일 생성

sudo nano /var/www/html/info.php

 

아래 php 코드 입력

 

10-3. 권한 설정

sudo chown www-data:www-data /var/www/html/info.php
sudo chmod 755 /var/www/html/info.php

 

10-4. 아파치 재시작

sudo systemctl restart apache2

 

10-5. 테스트

 - Windows 브라우저에서 IP/info.php 접속
 - PHP 정보 페이지(버전, 설정 등) 표시 확인

 

10-6. 간단한 PHP 페이지 추가

sudo nano /var/www/html/hello.php

 

아래 php 코드 입력

<!DOCTYPE html>
<html>
<body>
    <h1>Hello, PHP!</h1>
    <p>Current time: <?php echo date("Y-m-d H:i:s"); ?></p>
</body>
</html>

 

10-7. 권한 설정

sudo chown www-data:www-data /var/www/html/hello.php
sudo chmod 755 /var/www/html/hello.php

 

10-8. 테스트

 - Windows 브라우저에서 IP/info.php 접속
 - 현재 시간 표시 확인

모던 JS 문법

  • 모던 JS 문법은 ES6(ES2015) 이후 도입된 기능들로, 개발자 경험을 개선하고 코드를 간결하게 만듭니다.
  • Arrow Function, Spread 구문, Optional Chaining 외에도 Destructuring, Template Literals, Default Parameters 자주 사용되는 문법을 추가로 다루어보겠습니다.

Arrow Function (화살표 함수)
  - Arrow Function은 함수를 간결하게 정의하는 방법으로, function 키워드 대신 =>를 사용합니다. this 바인딩이 렉시컬(정적) 스코프를 따르는 점이 특징입니다.

 

기본사용

// 일반 함수
function add(a, b) {
    return a + b;
}

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

console.log(add(3, 5));      // 8
console.log(addArrow(3, 5)); // 8

 

매개변수가 하나일 경우

 - 괄호 생략 가능

const square = x => x * x;
console.log(square(4)); // 16

 

객체 반환

 -객체를 반환할 때는 소괄호로 감싸야 함

const getPerson = name => ({ name: name, age: 30 });
console.log(getPerson("홍길동")); // { name: "홍길동", age: 30 }

 

this 바인딩 차이

 - Arrow Functiuon은 this를 정의된 위치(렉시컬 스코프)에서 가져옵니다.

const obj = {
    name: "김철수",
    sayHello: function() {
        setTimeout(function() {
            console.log(this.name); // 일반 함수: this는 setTimeout의 this (undefined)
        }, 1000);

        setTimeout(() => {
            console.log(this.name); // Arrow Function: this는 obj (김철수)
        }, 1000);
    }
};
obj.sayHello();
// 출력:
// undefined
// 김철수

 

활용 예시

 - 배열 메소드와 함께

const numbers = [1, 2, 3, 4];
const doubled = numbers.map(num => num * 2);
console.log(doubled); // [2, 4, 6, 8]

 

 

Spread 구문(전개 연산자, ...)

 - Spread 구문은 배열이나 객체의 요소를 확장하거나 복사할 때 사용됩니다.

 

배열에서 사용

// 배열 복사
const arr1 = [1, 2, 3];
const arr2 = [...arr1]; // 얕은 복사
console.log(arr2); // [1, 2, 3]

// 배열 병합
const arr3 = [...arr1, 4, 5];
console.log(arr3); // [1, 2, 3, 4, 5]

// 배열 요소 전달
const sum = (a, b, c) => a + b + c;
const numbers = [1, 2, 3];
console.log(sum(...numbers)); // 6

 

객체에서 사용

// 객체 복사
const obj1 = { a: 1, b: 2 };
const obj2 = { ...obj1 }; // 얕은 복사
console.log(obj2); // { a: 1, b: 2 }

// 객체 병합
const obj3 = { ...obj1, c: 3 };
console.log(obj3); // { a: 1, b: 2, c: 3 }

// 기존 속성 덮어쓰기
const obj4 = { ...obj1, a: 10 };
console.log(obj4); // { a: 10, b: 2 }

 

활용 예시

 - 함수 인자

const user = { name: "홍길동", age: 25 };
const updatedUser = { ...user, age: 26, city: "서울" };
console.log(updatedUser); // { name: "홍길동", age: 26, city: "서울" }

 

 

Optional Chaining(?.)

 - Optional Chaining은 객체 속성에 안전하게 접근할 때 사용됩니다. 속성이 존재하지 않을 경우 undefined를 반환하며, 오류를 방지합니다.

 

기본 사용

const user = {
    name: "김철수",
    address: { city: "부산" }
};

console.log(user.address?.city);    // "부산"
console.log(user.phone?.number);    // undefined (오류 없이 접근)

 

중첩된 객체

const data = {
    user: {
        profile: { age: 30 }
    }
};

console.log(data.user?.profile?.age);      // 30
console.log(data.user?.settings?.theme);   // undefined

 

함수 호출에서 사용

const obj = {
    sayHello: () => "안녕!"
};

console.log(obj.sayHello?.());     // "안녕!"
console.log(obj.sayGoodbye?.());   // undefined

 

활용 예시

 - API 데이터 처리

const response = {
    user: { name: "홍길동" }
};

const userName = response.user?.name ?? "익명"; // Optional Chaining + Nullish Coalescing
console.log(userName); // "홍길동"

 

 

Destructuring(구조 분해 할당)

 - 객체나 배열에서 값을 추출해 변수로 바로 할당합니다.

 

객체 구조 분해

const person = { name: "홍길동", age: 22, city: "대구" };

// 기존 방식
// const name = person.name;
// const age = person.age;

// 구조 분해
const { name, age } = person;
console.log(name, age); // "홍길동" 22

// 별칭 사용
const { name: userName, city: location } = person;
console.log(userName, location); // "홍길동" "대구"

 

배열 구조 분해

const fruits = ["사과", "바나나", "포도"];

// 기존 방식
// const first = fruits[0];
// const second = fruits[1];

// 구조 분해
const [first, second] = fruits;
console.log(first, second); // "사과" "바나나"

// 일부 요소 건너뛰기
const [, , third] = fruits;
console.log(third); // "포도"

 

활용 예시

 - 함수 매개변수

const printUser = ({ name, age }) => {
    console.log(`${name}${age}살입니다.`);
};

const user = { name: "김철수", age: 28 };
printUser(user); // "김철수은 28살입니다."

 

 

Template Literals(템플릿 리터럴)

- 백틱(`)을 사용해 문자열을 더 간결하게 작성합니다. 변수나 표현식을 ${}로 삽입 가능합니다.

const name = "홍길동";
const age = 25;

// 기존 방식
const greeting = "안녕, " + name + "! 나이는 " + age + "살이야.";

// 템플릿 리터럴
const greetingModern = `안녕, ${name}! 나이는 ${age}살이야.`;
console.log(greetingModern); // "안녕, 홍길동! 나이는 25살이야."

// 표현식 사용
console.log(`2 + 3 = ${2 + 3}`); // "2 + 3 = 5"

 

활용 예시

 - HTML 생성

const user = { name: "이영희", age: 22 };
const html = `
    <div>
        <h1>${user.name}</h1>
        <p>나이: ${user.age}</p>
    </div>
`;
console.log(html);

 

 

Default Parameters(기본 매개변수)

 - 함수 매개변수에 기본값을 설정합니다.

function greet(name = "익명", greeting = "안녕") {
    return `${greeting}, ${name}!`;
}

console.log(greet());              // "안녕, 익명!"
console.log(greet("홍길동"));      // "안녕, 홍길동!"
console.log(greet("김철수", "hi")); // "hi, 김철수!"

 


 

비동기 관련 문법

  • 자바스크립트는 비동기 작업(예: 네트워크 요청, 파일 읽기, 타이머)을 처리하기 위해 Callback, Promise, async/await를 사용합니다. 
  • Callback은 오래된 방식으로, 현재는 Promise와 async/await가 주로 사용됩니다.

Callback (콜백 함수)

 - 비동기 작업이 완료된 후 실행할 함수를 전달하는 방식입니다. 하지만 "콜백 지옥(Callback Hell)" 문제가 발생할 수 있습니다.

 

예시

 - 콜백 지옥

setTimeout(() => {
    console.log("1단계 완료");
    setTimeout(() => {
        console.log("2단계 완료");
        setTimeout(() => {
            console.log("3단계 완료");
        }, 1000);
    }, 1000);
}, 1000);
// 출력:
// 1초 후: "1단계 완료"
// 2초 후: "2단계 완료"
// 3초 후: "3단계 완료"

 

Promise
 - Promise는 비동기 작업의 성공(resolve) 또는 실패(reject)를 처리하는 객체입니다. 콜백 지옥을 해결하는 데 유용합니다.

 

기본 사용

const myPromise = new Promise((resolve, reject) => {
    setTimeout(() => {
        const success = true; // 가정: 작업 성공
        if (success) {
            resolve("작업 성공!");
        } else {
            reject("작업 실패!");
        }
    }, 2000);
});

myPromise
    .then(result => console.log(result)) // 성공 시 실행
    .catch(error => console.error(error)); // 실패 시 실행
// 2초 후: "작업 성공!"

 

Promise 체이닝

const step1 = () => new Promise(resolve => {
    setTimeout(() => resolve("1단계 완료"), 1000);
});

const step2 = () => new Promise(resolve => {
    setTimeout(() => resolve("2단계 완료"), 1000);
});

const step3 = () => new Promise(resolve => {
    setTimeout(() => resolve("3단계 완료"), 1000);
});

step1()
    .then(result => {
        console.log(result);
        return step2();
    })
    .then(result => {
        console.log(result);
        return step3();
    })
    .then(result => console.log(result))
    .catch(error => console.error(error));
// 출력:
// 1초 후: "1단계 완료"
// 2초 후: "2단계 완료"
// 3초 후: "3단계 완료"

 

Promise.all

 - 여러 Promise를 병렬로 실행합니다

Promise.all([step1(), step2(), step3()])
    .then(results => console.log(results)) // 모든 Promise가 완료된 후 실행
    .catch(error => console.error(error));
// 출력: ["1단계 완료", "2단계 완료", "3단계 완료"]

 

async/await
 - async/await는 Promise를 더 간결하고 동기적으로 보이게 작성하는 문법입니다. async 함수는 항상 Promise를 반환합니다.

 

기본 사용

async function fetchData() {
    try {
        const result = await new Promise(resolve => {
            setTimeout(() => resolve("데이터 가져오기 성공!"), 2000);
        });
        console.log(result);
    } catch (error) {
        console.error("에러:", error);
    }
}

fetchData(); // 2초 후: "데이터 가져오기 성공!"

 

Promise 체이닝 대체

async function runSteps() {
    try {
        const result1 = await step1();
        console.log(result1);
        const result2 = await step2();
        console.log(result2);
        const result3 = await step3();
        console.log(result3);
    } catch (error) {
        console.error(error);
    }
}

runSteps();
// 출력:
// 1초 후: "1단계 완료"
// 2초 후: "2단계 완료"
// 3초 후: "3단계 완료"

 

병렬 실행 (Promise.all과 함께)

async function runStepsParallel() {
    try {
        const results = await Promise.all([step1(), step2(), step3()]);
        console.log(results);
    } catch (error) {
        console.error(error);
    }
}

runStepsParallel();
// 출력: ["1단계 완료", "2단계 완료", "3단계 완료"]

 

요약

  • 모던 JS 문법
     1. Arrow Function: 간결한 함수 정의, this 바인딩이 렉시컬 스코프
     2. Spread 구문: 배열/객체 복사 및 병합
     3. Optional Chaining: 안전한 속성 접근
     4. Destructuring: 객체/배열에서 값 추출
     5. Template Literals: 문자열 내 변수 삽입
     6. Default Parameters: 함수 매개변수 기본값 설정
  • 비동기 관련 문법
     1. Callback: 비동기 작업의 기본, 콜백 지옥 문제
     2. Promise: 성공/실패 처리, 체이닝 가능
     3. async/await: Promise를 동기적으로 보이게 작성, 에러 처리를 try/catch로 관리
     4. Promise.all: 여러 비동기 작업 병렬 실행

 


코드 이해해보기

  • 첫번째 코드
// 변수 선언: let을 사용하여 재할당 가능한 문자열 변수 message를 선언하고 "Hello, World!"로 초기화
let message = "Hello, World!";

// 상수 선언: const를 사용하여 재할당 불가능한 상수 pi를 선언하고 3.14로 초기화
const pi = 3.14;

// 변수 선언: let을 사용하여 재할당 가능한 불리언 변수 isActive를 선언하고 true로 초기화
let isActive = true;

// 객체 선언: let을 사용하여 재할당 가능한 객체 변수 user를 선언
// 객체는 name과 age 속성을 가짐
let user = {
    name: "Hong Gil-dong", // 속성: name은 "Hong Gil-dong" 문자열
    age: 25                // 속성: age는 25 숫자
};

// 배열 선언: let을 사용하여 재할당 가능한 배열 변수 colors를 선언
// 배열은 "red", "green", "blue" 문자열 요소를 포함
let colors = ["red", "green", "blue"];

// 함수 정의: greet라는 이름의 함수를 정의, name 매개변수를 받음
function greet(name) {
    // 콘솔에 "Hello, "와 name, "!"를 연결한 문자열 출력
    console.log("Hello, " + name + "!");
}

// 함수 호출: greet 함수를 호출하며 "Anna"를 인자로 전달
// 콘솔에 "Hello, Anna!"가 출력됨
greet("Anna");

 

  • 두번째 코드
// 객체 선언: let을 사용하여 재할당 가능한 객체 변수 student를 선언
let student = {
    name: "Kim Yoon-sung", // 속성: name은 "Kim Yoon-sung" 문자열
    major: "Computer Science", // 속성: major는 "Computer Science" 문자열
    // 메소드 정의: getIntroduction이라는 함수를 속성으로 정의
    getIntroduction: function() {
        // 콘솔에 "My name is ", this.name, " and I study ", this.major, "."를 연결한 문자열 출력
        // this는 student 객체를 참조하므로 this.name은 "Kim Yoon-sung", this.major는 "Computer Science"
        console.log("My name is " + this.name + " and I study " + this.major + ".");
    }
};

// 메소드 호출: student 객체의 getIntroduction 메소드를 호출
// 콘솔에 "My name is Kim Yoon-sung and I study Computer Science."가 출력됨
student.getIntroduction();

// 배열 선언: let을 사용하여 재할당 가능한 배열 변수 numbers를 선언
// 배열은 숫자 1, 2, 3, 4, 5를 요소로 포함
let numbers = [1, 2, 3, 4, 5];

// 배열 메소드 사용: push 메소드를 호출하여 배열 끝에 6을 추가
numbers.push(6);

// 콘솔 출력: numbers 배열을 출력
// push로 6이 추가되었으므로 [1, 2, 3, 4, 5, 6]이 출력됨
console.log(numbers);

 

  • 세번째 코드
// setTimeout: 지정된 시간(밀리초) 후에 함수를 한 번 실행하는 내장 함수
// 3000ms(3초) 후에 익명 함수를 실행
setTimeout(function() {
    // 3초 후에 콘솔에 메시지 출력
    console.log("3초가 지났어요!");
}, 3000);

// 변수 선언: 카운트를 저장할 변수 count를 0으로 초기화
let count = 0;

// setInterval: 지정된 시간 간격(밀리초)마다 함수를 반복 실행하는 내장 함수
// 1000ms(1초)마다 익명 함수를 실행, intervalId에 interval의 ID를 저장
let intervalId = setInterval(function() {
    // count를 1씩 증가
    count++;
    // 현재 count 값을 포함한 메시지 출력
    console.log(count + "초마다 메시지가 출력됩니다.");
    // count가 5 이상이면 반복 중지
    if (count >= 5) {
        // clearInterval: intervalId를 사용해 setInterval 반복을 중지
        clearInterval(intervalId);
    }
}, 1000);

// 배열 선언: fruits 배열에 문자열 요소 "apple", "banana", "cherry"를 포함
let fruits = ["apple", "banana", "cherry"];

// forEach: 배열의 각 요소에 대해 주어진 함수를 실행하는 배열 메소드
// 각 요소(fruit)를 콘솔에 출력
fruits.forEach(function(fruit) {
    console.log(fruit);
    // 출력:
    // apple
    // banana
    // cherry
});

// 배열 선언: numbers 배열에 숫자 요소 1, 2, 3, 4, 5를 포함
let numbers = [1, 2, 3, 4, 5];

// map: 배열의 각 요소에 대해 주어진 함수를 적용한 결과를 새로운 배열로 반환
// 각 요소(number)를 2배로 만들어 새로운 배열 doubledNumbers에 저장
let doubledNumbers = numbers.map(function(number) {
    return number * 2; // 각 숫자를 2배로 변환
});
// doubledNumbers 배열 출력: [2, 4, 6, 8, 10]
console.log(doubledNumbers);

// filter: 배열의 각 요소에 대해 주어진 조건을 만족하는 요소만 새로운 배열로 반환
// 짝수인 요소만 필터링하여 새로운 배열 evenNumbers에 저장
let evenNumbers = numbers.filter(function(number) {
    return number % 2 === 0; // number가 짝수일 경우 true 반환
});
// evenNumbers 배열 출력: [2, 4]
console.log(evenNumbers);

 


다음과 같은 내용에 도전해봅시다.

var, let과 const의 차이점 이해하기

  var let const
스코프 함수 스코프 블록 스코프 블록 스코프
재선언 가능 불가 불가
재할당 가능 가능 불가
호이스팅 undefined TDZ TDZ
권장 여부 비권장 권장 권장(상수용

 

 

Arrow Function 이해하기
- Arrow Function(화살표 함수)은 ES6에서 도입된 간결한 함수 표현식입니다. function 키워드 대신 =>를 사용하며, this 바인딩 방식이 다릅니다.

장점 주의점
간결한 문법 this가 고정되므로 객체 메소드나 이벤트 핸들러에서 부적합할 수 있음
this가 렉시컬 스코프로 고정되어 예측 가능 arguments 객체 사용 불가
배열 메소드(mpa, forEach 등)와 함께 사용 시 유용  

 

-  예시

const obj = {
    name: "홍길동",
    sayHello: function() {
        setTimeout(function() {
            console.log(this.name); // 일반 함수: this는 setTimeout의 this (undefined)
        }, 1000);

        setTimeout(() => {
            console.log(this.name); // Arrow Function: this는 obj (김철수)
        }, 1000);
    }
};
obj.sayHello();
// 출력:
// undefined
// 홍길동

 

 

룰렛 게임 완성하기

주어진 코드

<!DOCTYPE html>
<html>
  <head>
    <title>Roulette Game</title>
    <script></script>
  </head>
  <body>
    <div id="roulette">1</div>
    <button id="stopButton">정지</button>

    <script>
      const values = [1, 2, 3, 4, 5, 6];

      const rouletteDisplay = document.getElementById("roulette");

      let intervalId = null;

      let currentIndex = 0;

      function startRoulette() {
        intervalId = //interval 설정하기
      }

      document.getElementById("stopButton").addEventListener("click", () => {
        clearInterval(intervalId);

        alert("선택된 숫자: " + values[currentIndex]);
      });

      startRoulette();
    </script>
  </body>
</html>

 

완성된 코드

<!DOCTYPE html>
<html>
  <head>
    <title>Roulette Game</title>
  <style>
      #roulette { /* 룰렛 숫자가 표시될 div 요소의 스타일 */
        font-size: 48px; /* 글자 크기를 48px로 설정 */
        text-align: center; /* 텍스트를 가운데 정렬 */
        margin: 20px; /* 외부 여백을 20px로 설정 */
        padding: 20px; /* 내부 여백을 20px로 설정 */
        border: 2px solid #333; /* 2px 두께의 검은색(#333) 테두리 */
        width: 100px; /* div의 너비를 100px로 설정 */
        margin: 20px auto; /* 상하 여백 20px, 좌우 여백 auto로 가운데 정렬 */
      }
      #stopButton { /* 정지 버튼의 스타일 */
        display: block; /* 버튼을 블록 요소로 설정하여 한 줄 차지 */
        margin: 0 auto; /* 좌우 여백 auto로 가운데 정렬 */
        padding: 10px 20px; /* 상하 10px, 좌우 20px 내부 여백 */
        font-size: 16px; /* 글자 크기를 16px로 설정 */
        border: none; /* 테두리 제거 */
        cursor: pointer; /* 마우스 커서를 포인터로 변경 (클릭 가능 표시) */
      }
    </style>
  </head>
  <body>
    <!-- 룰렛 숫자가 표시될 div -->
    <div id="roulette">1</div>
    <!-- 룰렛을 멈추는 버튼 -->
    <button id="stopButton">정지</button>

    <script>
      // 룰렛에 표시될 숫자 배열 (const로 선언: 재할당 불가)
      const values = [1, 2, 3, 4, 5, 6];

      // DOM 요소: 룰렛 숫자가 표시될 div 요소 가져오기
      const rouletteDisplay = document.getElementById("roulette");

      // intervalId 변수: setInterval의 ID를 저장 (let으로 선언: 재할당 필요)
      let intervalId = null;

      // currentIndex 변수: 현재 표시 중인 숫자의 인덱스 (let으로 선언: 재할당 필요)
      let currentIndex = 0;

      // startRoulette 함수: 룰렛을 시작하는 함수
      function startRoulette() {
        // setInterval: 100ms마다 숫자를 변경하는 타이머 설정
        intervalId = setInterval(function() { // Arrow Function 대신 일반 function 사용
          // 현재 인덱스를 1 증가
          currentIndex = currentIndex + 1;

          // 만약 currentIndex가 배열의 길이(6) 이상이 되면 0으로 초기화 (순환)
          if (currentIndex >= values.length) { // % 연산 대신 if 조건문으로 변경
            currentIndex = 0; // 배열의 처음으로 돌아감
          }

          // 룰렛 div에 현재 숫자 표시
          rouletteDisplay.textContent = values[currentIndex]; // 현재 인덱스의 숫자를 div에 표시
        }, 100); // 100ms 간격으로 실행 (0.1초마다 숫자 변경)
      }

      // stopButton 클릭 이벤트 리스너: 버튼 클릭 시 룰렛 멈춤
      document.getElementById("stopButton").addEventListener("click", () => {
        // clearInterval: intervalId를 사용해 setInterval 중지
        clearInterval(intervalId);
        // 현재 선택된 숫자를 알림창으로 표시
        alert("선택된 숫자: " + values[currentIndex]);
      });

      // 페이지 로드 시 룰렛 자동 시작
      startRoulette();
    </script>
  </body>
</html>

자바스크립트(Javascript)

  • 자바스크립트는 웹 페이지를 동적으로 만들어주는 스크립팅 언어입니다. 웹 브라우저 내에서 다양한 기능을 실행할 수 있으며, 이벤트 처리, 데이터 저장, DOM 조작 등을 가능하게 해줍니다.

 

자바스크립트의 기본 문법

  • 자바스크립트는 웹 페이지를 동적으로 만들기 위한 스크립팅 언어로, 주로 브라우저에서 실행됩니다.
  • HTML 문서에 <script> 태그를 사용해 삽입하거나, 별도의 .js 파일로 분리해 연결할 수 있습니다.
<!DOCTYPE html>
<html lang="ko">
<head>
    <meta charset="UTF-8">
    <title>JS 기본 문법</title>
</head>
<body>
    <h1>자바스크립트 테스트</h1>
    <script>
        // 콘솔에 메시지 출력
        console.log("안녕, 자바스크립트!");

        // 경고창 표시
        alert("환영합니다!");
    </script>
</body>
</html>
  • 주석: // (한 줄 주석) 또는 /* */ (여러 줄 주석) 사용.
  • 세미콜론(;): 문장의 끝을 나타내며, 생략 가능하지만 명확성을 위해 사용하는 것이 좋습니다.
  • 대소문자 구분: 자바스크립트는 대소문자를 구분합니다. 예: myVarmyvar는 다른 변수입니다.

 

변수 선언과 데이터 타입

  • 자바스크립트에서 변수는 let, const, var 키워드로 선언할 수 있습니다.
  • var는 ES6 이후 권장되지 않으며, letconst를 주로 사용합니다.
  • 변수 선언
      - let: 재할당 가능한 변수.
      -
    const: 재할당 불가능한 상수 (단, 객체/배열 내부 값은 변경 가능).
let name = "홍길동"; // 변수 선언 및 초기화
name = "김철수";     // 재할당 가능
console.log(name);   // "김철수"

const age = 25;      // 상수 선언
// age = 30;         // 오류: const는 재할당 불가
console.log(age);    // 25
  • 자바스크립트는 동적 타입 언어로, 변수의 타입이 실행 중에 결정됩니다.
    - 기본(원시) 타입
        1. string: 문자열 (예: "안녕", '안녕')
        2. number: 숫자 (예: 42, 3.14)
        3. boolean: 참/거짓 (true, false)
        4. undefined: 값이 정의되지 않음
        5. null: 값이 없음
        6. symbol: 고유한 값 (ES6 도입)
    - 참조 타입
        1. object: 객체, 배열, 함수 등
let str = "바나나";        // string
let num = 123;            // number
let bool = true;          // boolean
let undef;                // undefined
let nul = null;           // null
let sym = Symbol("id");   // symbol

console.log(typeof str);  // "string"
console.log(typeof num);  // "number"

 

 

함수 정의와 호출

  • 함수는 특정 작업을 수행하는 코드 블록으로, function 키워드 또는 Arrow Function (화살표 함수, ES6 도입)으로 정의할 수 있습니다.
// 일반 함수 정의
function sayHello(name) {
    return "안녕, " + name + "!";
}
console.log(sayHello("홍길동")); // "안녕, 홍길동!"

// Arrow Function (화살표 함수)
const add = (a, b) => a + b;
console.log(add(3, 5)); // 8

 

Arrow Function 특징
 -  function 키워드 대신 => 사용
 -  한 줄일 경우 중괄호와 return 생략 가능
 -  this 바인딩이 다르게 동작 (렉시컬 this)

const multiply = (x, y) => {
    const result = x * y;
    return result;
};
console.log(multiply(4, 5)); // 20

 

 

객체와 배열의 사용 방법

  • 자바스크립트에서 데이터를 구조화하기 위해 객체와 배열을 사용합니다.

객체 (Object)
 - 객체는 키-값 쌍으로 데이터를 저장합니다.

// 객체 생성
const person = {
    name: "홍길동",
    age: 25,
    city: "서울",
    greet: function() {
        return `안녕, 나는 ${this.name}이야!`;
    }
};

// 객체 속성 접근
console.log(person.name);        // "홍길동"
console.log(person["age"]);      // 25
console.log(person.greet());     // "안녕, 나는 홍길동이야!"

// 속성 추가 및 수정
person.job = "개발자";
person.age = 26;
console.log(person); // {name: "홍길동", age: 26, city: "서울", greet: ƒ, job: "개발자"}

 

배열(Array)
 - 배열은 순서가 있는 데이터 목록 입니다.

// 배열 생성
const fruits = ["사과", "바나나", "오렌지"];

// 배열 요소 접근
console.log(fruits[0]); // "사과"

// 배열 메소드 사용
fruits.push("포도");    // 배열 끝에 추가
console.log(fruits);    // ["사과", "바나나", "오렌지", "포도"]

fruits.pop();           // 배열 끝 요소 제거
console.log(fruits);    // ["사과", "바나나", "오렌지"]

// 배열 순회
fruits.forEach(fruit => console.log(fruit));
// 출력:
// 사과
// 바나나
// 오렌지

 

Spread 구문 (ES6)
 - 배열이나 객체를 복사하거나 병합할 때 유용합니다.

const arr1 = [1, 2, 3];
const arr2 = [...arr1, 4, 5]; // 배열 복사 및 확장
console.log(arr2); // [1, 2, 3, 4, 5]

const obj1 = { a: 1, b: 2 };
const obj2 = { ...obj1, c: 3 }; // 객체 복사 및 확장
console.log(obj2); // { a: 1, b: 2, c: 3 }

 

Optional Chaining (ES2020)
 - 객체 속성에 안전하게 접근할 때 유용합니다.
const user = { name: "홍길동", address: { city: "서울" } };
console.log(user.address?.city);    // "서울"
console.log(user.phone?.number);    // undefined (오류 없이 안전하게 접근)

 

 

자주 사용하는 내장 함수들

  • 자바스크립트는 다양한 내장 함수와 메소드를 제공합니다.

 

문자열 관련

const str = "Hello, JavaScript!";

// 문자열 길이
console.log(str.length); // 17

// 문자열 자르기
console.log(str.slice(0, 5)); // "Hello"

// 대소문자 변환
console.log(str.toUpperCase()); // "HELLO, JAVASCRIPT!"
console.log(str.toLowerCase()); // "hello, javascript!"

 

숫자 관련

const num = 3.14159;

// 소수점 자르기
console.log(num.toFixed(2)); // "3.14"

// 랜덤 숫자 생성
console.log(Math.random()); // 0 이상 1 미만의 랜덤 숫자

// 절대값, 반올림
console.log(Math.abs(-5));  // 5
console.log(Math.round(3.6)); // 4

 

배열 관련

const numbers = [1, 2, 3, 4, 5];

// 배열 필터링
const evens = numbers.filter(num => num % 2 === 0);
console.log(evens); // [2, 4]

// 배열 매핑
const doubled = numbers.map(num => num * 2);
console.log(doubled); // [2, 4, 6, 8, 10]

 

타이머 함수

// 2초 후 실행
setTimeout(() => console.log("2초 경과!"), 2000);

// 1초마다 반복
let count = 0;
const interval = setInterval(() => {
    console.log(`카운트: ${++count}`);
    if (count === 5) clearInterval(interval); // 5번 후 중지
}, 1000);

 

HTML 문서의 구조

  • HTML 문서는 DOCTYPE 선언으로 시작합니다. 이는 웹 브라우저에게 이 문서가 HTML5 문서임을 알려주는 역할을 합니다.
<!DOCTYPE html>
<html>
<head>
  <title>제목</title>
</head>
<body>
  <h1>h1</h1>
</body>
</html>

 

 

기본적인 HTML 태그들

  • <html>: HTML 문서의 시작과 끝을 나타냅니다.
  • <head>: 웹 페이지의 메타 정보를 담는 곳입니다. <title>, <style>, <meta>, <link>, <script> 등의 태그를 포함할 수 있습니다.
  • <title>: 웹 페이지의 제목을 설정합니다. 브라우저의 탭에 표시됩니다.
  • <body>: 웹 페이지의 본문을 나타내는 부분입니다. 웹 브라우저에 실제로 표시되는 내용을 담습니다.
  • <h1> ~ <h6>: 제목을 나타내는 태그입니다. <h1>이 가장 큰 제목이며, <h6>까지 숫자가 커질수록 글자 크기가 작아집니다.
  • <p>: 문단을 나타내는 태그입니다. 텍스트를 담는 데 사용됩니다.
  • <a>: 하이퍼링크를 만드는 태그입니다. href 속성에 링크할 URL을 지정합니다.
  • <img>: 이미지를 삽입하는 태그입니다. src 속성에 이미지 파일의 경로를 지정합니다.
  • <div>: 구역이나 섹션을 만드는 태그입니다. CSS와 함께 사용하여 웹 페이지의 레이아웃을 디자인합니다.
  • <span>: 인라인 요소를 그룹화하는 태그입니다. 텍스트 또는 다른 인라인 요소에 스타일을 적용하기 위해 사용합니다.
  • <table>: 테이블을 생성하는 태그입니다. <tr>, <td>, <th> 등의 태그와 함께 사용합니다.
        -   <tr>: 테이블의 행을 정의합니다.
        -    <td>: 테이블의 데이터 셀을 정의합니다.
        -    <th>: 테이블의 헤더 셀을 정의합니다.
  • <ul>과 <ol>: 목록을 만드는 태그입니다. <li> 태그와 함께 사용합니다. <ul>은 순서가 없는 목록을, <ol>은 순서가 있는 목록을 만듭니다.
  • <li>: 목록의 항목을 정의하는 태그입니다.
  • <form>: 사용자 입력을 위한 HTML 양식을 만드는 태그입니다. <input>, <textarea>, <button> 등의 태그와 함께 사용합니다.
  • <input>: 사용자 입력을 받는 필드를 만드는 태그입니다. type 속성을 이용해 다양한 종류의 입력 필드를 만들 수 있습니다.

더 많은 태그들은 아래 링크에서 확인할 수 있습니다. 바로가기

 

HTML elements reference - HTML: HyperText Markup Language | MDN

This page lists all the HTML elements, which are created using tags.

developer.mozilla.org

 


실습

  • 아래의 사진을 참고하여, 사과와 바나나를 소개하는 패이지를 제작하여 봅시다.
    - 두 개의 페이지는 서로 a태그를 이용하여 서로 연결되어 있습니다.
    - 제출 버튼을 눌렀을 때의 동작은 구현하지 않아도 좋습니다.

 

바나나페이지 HTML 코드(banana_page.html)

<!-- HTML5 문서임을 선언 -->
<!DOCTYPE html>
<html>
<head>
<!-- 문서의 문자 인코딩을 UTF-8로 설정 -->
    <meta charset="UTF-8"> 
    <style type="text/css">
        /* .my-box 클래스를 가진 요소에 바깥쪽 여백(margin) 20px 적용 */
        .my-box { margin:20px; }
    </style>
</head>

<body>
<!-- 전체 콘텐츠를 감싸는 div 박스, 클래스 이름은 my-box -->
<div class="my-box">

    <!-- 메인 제목과 수평선 -->
    <h1>바나나!</h1>
    <hr/>

    <!-- 바나나에 대한 소개 섹션 -->
    <h2>바나나에 대하여</h2>
    <p>
        바나나는 전 세계에서 가장 인기 있는 과일 중 하나입니다.
        바나나는 건강에 보호적인 영향을 미칠 수 있는 필수 영양요소를 함유하고 있습니다.
    </p>

    <!-- 이미지 삽입 -->
    <img src="https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcQCPa_h8cs0hRiNhReczSVsxqlK-3hGUBbQ2w&s" alt="바나나 이미지">

    <!-- 목록 태그 사용 -->
    <h2>바나나의 건강 이점</h2>
    <ul>
        <li>높은 섬이질 함량</li> <!-- 섬유질이 많음 -->
        <li>심장 건강</li>         <!-- 심장 건강에 도움 -->
        <li>소화 촉진</li>         <!-- 소화에 도움 -->
    </ul>

    <h2>맛있는 바나나 레시피</h2>
    <label>
        이메일: <br>
        <input type="text" name="member_email" id="member_email"> <br>
    </label>
    <label>
        당신의 좋아하는 바나나 레시피를 공유하세요. <br>
        <textarea rows="5" cols="60" name="contents"></textarea> <br>
    </label>
    <!-- 제출 버튼 (아직 동작은 없음) -->
    <button>제출</button>

    <!-- 사과 페이지로 이동하는 링크 -->
    <p><a href="apple_page.html">사과</a>에 대하여</p>

</div>
</body>
</html>

 

사과페이지 HTML 코드(apple_page.html)

<!-- HTML5 문서임을 선언 -->
<!DOCTYPE html>
<html>
<head>
    <!-- 문서의 문자 인코딩을 UTF-8로 설정 -->
    <meta charset="UTF-8">
    <style type="text/css">
        /* .my-box 클래스를 가진 요소에 바깥쪽 여백(margin) 20px 적용 */
        .my-box { margin:20px; }
    </style>
</head>

<body>
<!-- 전체 콘텐츠를 감싸는 div 박스, 클래스 이름은 my-box -->
<div class="my-box">

    <!-- 메인 제목과 수평선 -->
    <h1>사과!</h1>
    <hr/>

    <!-- 사과에 대한 소개 섹션 -->
    <h2>사과에 대하여</h2>
    <p>
        사과는 전 세계에서 널리 사랑받는 과일 중 하나입니다.
        사과는 다양한 영양소를 함유하고 있어 건강에 좋습니다.
    </p>

    <!-- 이미지 삽입 -->
    <img src="https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcTTqBOfkJOBqR4ISvqOQBR8zG1NBlJfSKIWuw&s" alt="사과 이미지">

    <!-- 목록 태그 사용 -->
    <h2>사과의 건강 이점</h2>
    <ul>
        <li>높은 식이섬유 함량</li>
        <li>다양한 비타민과 미네랄</li>
        <li>항산화 물질 풍부</li>
    </ul>

    <h2>맛있는 사과 레시피</h2>
    <label>
        이메일: <br>
        <input type="text" name="member_email" id="member_email"> <br>
    </label>
    <label>
        당신의 좋아하는 사과 레시피를 공유하세요. <br>
        <textarea rows="5" cols="60" name="contents"></textarea> <br>
    </label>
    <!-- 제출 버튼 (아직 동작은 없음) -->
    <button>제출</button>

    <!-- 바나나 페이지로 이동하는 링크 -->
    <p><a href="banana_page.html">바나나</a>에 대하여</p>

</div>
</body>
</html>

 

HTML, CSS, JS의 기본 개념, 용도, 사용방법, 관계

  • HTML (HyperText Markup Language)
    - HTML은 웹페이지의 뼈대를 만드는 언어입니다.
    - 텍스트, 이미지, 링크, 버튼 등 페이지에 보이는 요소들을 배치하고 구조를 잡는 역할을 하며, 집을 짓는다면 벽과 기둥, 창문 같은 부분이 HTML입니다.
    - 예를 들어, 글을 쓸 때 제목을 정하거나 문단을 나누는 것도 HTML 태그를 이용하며, 이미지를 넣거나, 다른 페이지로 이동하는 링크를 만드는 것도 모두 HTML로 작성합니다.
  • CSS (Cascading Style Sheets)
    - CSS는 HTML로 만들어진 구조에 디자인을 입히는 역할을 합니다.
    - 색상, 글씨 크기, 글꼴, 여백, 레이아웃 등 시각적으로 보이는 모든 요소를 꾸며주는 스타일 시트 언어입니다.
    - 예를 들어, 배경을 파란색으로 바꾸거나 버튼을 둥글게 만들고, 글자를 가운데 정렬하는 등의 작업이 CSS를 통해 이루어집니다.
    - CSS 덕분에 웹페이지가 더 아름답고 보기 좋게 바뀌며, 사용자에게 좋은 인상을 줄 수 있습니다.
  • JavaScript
    - JavaScript웹페이지에 동적인 기능을 추가하는 프로그래밍 언어입니다.
    - 사용자의 행동(버튼 클릭, 입력 등)에 따라 페이지가 반응하도록 만들어줍니다.
    - 예를 들어, 버튼을 클릭했을 때 알림 창이 뜨거나, 입력한 값을 검사하는 기능, 페이지 내용이 실시간으로 변경되는 인터랙션 등이 모두 JavaScript의 역할입니다.
    - JS가 없으면 웹페이지는 단순히 정보를 보여주는 정적인 페이지에 머무르지만, JS를 사용하면 살아있는 웹페이지를 만들 수 있습니다.
구분 HTML CSS JavaScript
역할 구조 (Structure) 스타일링 (Design) 동작 (Interaction)
용도 웹페이지의 뼈대를 만든다 (텍스트, 이미지, 링크 등) 웹페이지를 꾸민다 (색상, 크기, 배치 등) 웹페이지를 동적으로 만든다 (버튼 클릭 시 이벤트, 데이터 처리 등)
작성 위치 .html 파일 .css 파일 .js 파일
<html>, <body> 내부에 작성 <style> 태그 안 <script> 태그 안
  link로 연결 src로 연결
관계 HTML이 기본 골격, CSS가 꾸밈, JS가 동작을 부여하는 관계    
세 가지가 함께 웹페이지를 완성함  

 

  • 웹페이지 제작에 필요한 기본적인 HTML 태그들

태그 설명 예시
<html> HTML 문서의 루트 <html> ... </html>
<head> 메타데이터, 외부 파일 연결 <head> <title>문서 제목</title> </head>
<body> 실제 페이지에 보이는 내용 <body> ... </body>
<h1> ~ <h6> 제목 (숫자가 작을수록 크다) <h1>큰 제목</h1>
<p> 문단 (Paragraph) <p>문장입니다.</p>
<a> 링크 (Anchor) 클릭
<img> 이미지 삽입 <img src="image.jpg" alt="설명">
<ul>, <ol>, <li> 목록 (순서 없는/있는) <ul><li>아이템</li></ul>
<table> <table><tr><td>셀</td></tr></table>
<div> 블록 영역 그룹화 <div>내용</div>
<span> 인라인 영역 그룹화 <span>내용</span>
<form>, <input> 입력 폼 <form><input type="text"></form>
<button> 버튼 <button>클릭</button>

 

  • CSS의 기본적인 문법과 속성들
    1. 주요 선택자
       - * : 모든 요소
       - 태그명 : 특정 태그
       - .클래스명 : 클래스 선택
       - #아이디명 : 아이디 선택
    2. 주요 속성들
    속성 설명 예시
    color 글자 색상 color: red;
    background-color 배경 색상 background-color: yellow;
    font-size 글자 크기 font-size: 16px;
    margin 바깥 여백 margin: 10px;
    padding 안쪽 여백 padding: 10px;
    border 테두리 border: 1px solid black;
    width, height 너비, 높이 width: 100px; height: 50px;
    display 요소 표시 방식 display: block;
    flex, grid 레이아웃 display: flex;

    3. 
    문법
선택자 {
  속성: 값;
  속성: 값;
}

 

 

  • JS의 기본적인 문법과 함수들
    1. 주요 기능 및 함수

    기능 설명 예시
    변수 선언 데이터를 저장 let name = "철수";
    함수 선언 코드 블록 저장 후 실행 function sayHi() { alert("안녕"); }
    조건문 분기 처리 if (a > 5) { ... } else { ... }
    반복문 반복 실행 for (let i = 0; i < 5; i++) { ... }
    이벤트 처리 사용자 상호작용 감지 element.addEventListener("click", 함수);
    DOM 조작 HTML 요소 변경 document.querySelector("p").textContent = "변경된 내용";
    내장 함수 alert(), console.log(), prompt() 등  

    2. 기본 문법
// 변수
let a = 10;
const b = 20;

// 함수
function greet() {
  console.log("안녕하세요!");
}

// 이벤트
document.querySelector("button").addEventListener("click", greet);

 

 


다음을 이해해 봅시다.

<!-- HTML 문서의 유형을 선언 (HTML5 문서임을 브라우저에 알림) -->
<!DOCTYPE html>

<!-- HTML 문서의 시작 -->
<html>

<!-- 문서의 메타데이터(스타일, 제목, 스크립트 등)를 담는 head 부분 -->
<head>
    <!-- 문서 내부에 CSS 스타일을 정의 -->
    <style>
        /* body 태그의 배경색을 연한 파란색으로 설정 */
        body {background-color: powderblue;}
        /* h1 태그(제목)의 글자색을 파란색으로 설정 */
        h1   {color: blue;}
        /* p 태그(문단)의 글자색을 빨간색으로 설정 */
        p    {color: red;}
    </style>
</head>

<!-- 문서의 본문 시작 -->
<body>

<!-- h1 태그: 큰 제목 표시 -->
<h1>h1태그</h1>

<!-- p 태그: 단락(문장) 표시 -->
<p>p 태그</p>

<!-- 버튼 생성, 클릭하면 changeColor() 함수 실행 -->
<button onclick="changeColor()">배경색 변경</button>

<!-- JavaScript 코드를 작성하는 영역 -->
<script>
    // changeColor 함수 정의
    function changeColor() {
        // 문서의 body 배경색을 노란색으로 변경
        document.body.style.backgroundColor = "yellow";
    }
</script>

<!-- 문서의 본문 종료 -->
</body>

<!-- HTML 문서 종료 -->
</html>

 

실행화면

<!-- HTML5 문서라는 것을 선언 -->
<!DOCTYPE html>

<!-- HTML 문서의 시작 -->
<html>

<!-- 문서의 본문 시작 -->
<body>

<!-- h2 태그: 중간 크기의 제목을 표시 -->
<h2>배열 이해하기</h2>

<!-- p 태그: id="demo" 인 문단 -->
<p id="demo"></p>

<!-- JavaScript 코드를 작성하는 영역 -->
<script>
    // 'cars'라는 이름의 배열을 선언하고, 문자열 요소 3개를 저장
    var cars = ["Saab", "Volvo", "BMW"];

    // id가 "demo"인 HTML 요소를 찾아서, cars 배열의 내용을 표시
    document.getElementById("demo").innerHTML = cars;
    // 결과: Saab,Volvo,BMW (배열 요소가 쉼표로 구분되어 출력됨)
</script>

<!-- 문서의 본문 종료 -->
</body>

<!-- HTML 문서 종료 -->
</html>

 

실행화면

 

 


실습

  • 자신을 소개하는 페이지를 작성해 봅시다(형식 자유)
<!-- HTML5 문서임을 선언 -->
<!DOCTYPE html>
<html> <!-- HTML 문서의 시작 -->

<head> <!-- 문서의 설정 및 스타일을 지정하는 부분 -->
    <title>나의 소개</title> <!-- 브라우저 탭에 표시될 제목 -->
    <style> <!-- CSS 스타일 정의 시작 -->
        body { /* body 태그 전체에 적용할 스타일 */
            background-color: #f0f8ff; /* 배경 색을 연한 파란색으로 지정 */
            font-family: Arial, sans-serif; /* 글꼴을 Arial 또는 sans-serif로 지정 */
            padding: 20px; /* 바깥 여백을 20픽셀로 지정 */
        }
        h1 { /* h1 태그에 적용할 스타일 */
            color: #3333cc; /* 글자 색을 파란 계열로 지정 */
        }
        p { /* p 태그(문단)에 적용할 스타일 */
            color: #000000; /* 글자 색을 검정으로 지정 */
            font-size: 18px; /* 글자 크기를 18픽셀로 지정 */
            line-height: 1.6; /* 줄 간격을 1.6으로 지정 */
        }
        button { /* 버튼에 적용할 스타일 */
            margin-top: 20px; /* 버튼 위 여백 20픽셀 */
            padding: 10px 20px; /* 안쪽 여백 위아래 10픽셀, 좌우 20픽셀 */
            background-color: #4CAF50; /* 배경 색을 녹색으로 지정 */
            color: white; /* 글자 색을 흰색으로 지정 */
            border: none; /* 테두리를 없앰 */
            border-radius: 8px; /* 모서리를 둥글게 8픽셀 */
            cursor: pointer; /* 마우스 올릴 때 포인터 모양으로 변경 */
        }
        button:hover { /* 버튼에 마우스를 올렸을 때 스타일 */
            background-color: #45a049; /* 조금 더 짙은 녹색으로 변경 */
        }
    </style> <!-- CSS 스타일 정의 끝 -->
</head> <!-- head 영역 끝 -->

<body> <!-- 웹페이지 본문 시작 -->

    <h1>안녕하세요! 👋</h1> <!-- 큰 제목으로 인사말 표시 -->
    
    <p>저는 보안관제로 근무중이며,모의해킹으로 이직을 하기 위해<br> 
       <strong>ELITE HACKER Bootcamp 4th</strong>를 배우고 있는 직장인입니다.</p> 
       <!-- 본문 소개 문장, <strong>은 강조 (굵게), <br>은 줄바꿈 -->
       
    <p>진행중인 <strong>pre.Web</strong>, <strong>pre.Rev</strong> 모두에 관심이 있어요.</p> 
    <!-- pre.Web, pre.Rev 과정에 관심 있다는 소개 -->

    <p><strong>pre.Web</strong> 코스를 다 끝내고 나면 <strong>pre.Rev</strong> 코스도 도전할 예정입니다!</p> 
    <!-- pre.Web 끝난 후 pre.Rev 도전할 계획 -->

    <p>취미는 🎮 게임, 🎤 노래 부르기 입니다!</p> 
    <!-- 취미 소개 -->

    <button onclick="alert('응원합니다! 화이팅!')">응원 받기</button> 
    <!-- 버튼 클릭 시 "응원합니다! 화이팅!" 알림이 뜨는 기능 -->

</body> <!-- 웹페이지 본문 끝 -->

</html> <!-- HTML 문서 끝 -->

 

실행결과

 

  • 간단한 계산기 페이지 만들기

<!-- HTML5 문서임을 선언 -->
<!DOCTYPE html>

<html>
<head>
	<!-- 브라우저 탭 제목을 "간단한 계산기"로 설정 -->
    <title>간단한 계산기</title>
</head>
<body>
	<!-- 페이지의 메인 제목 -->
    <h1>간단한 계산기</h1>

    <!-- 사용자가 계산식을 입력하는 텍스트 박스 -->
    <input type="text" id="expression" placeholder="계산식을 입력하세요">

    <!-- 버튼 클릭 시 calculate() 함수를 호출하여 계산 실행 -->
    <button onclick="calculate()">계산!</button>

    <!-- 결과가 출력될 영역 -->
    <p>Result: <span id="result"></span></p>

    <script>
        // 계산을 수행하는 함수 정의
        function calculate() {
            // 사용자가 입력한 수식을 가져옴
            var expression = document.getElementById("expression").value;

            // eval() 함수를 이용하여 수식 계산
            var result = eval(expression);

            // 계산된 결과를 HTML의 result 영역에 출력
            document.getElementById("result").innerText = result;
        }
    </script>

</body>
</html>

 

실행결과

프록시(Proxy)

  • 프록시(Proxy)중간에서 대신 요청을 전달하고 응답을 받아주는 중계 서버입니다.
  • 클라이언트(나)와 서버(목표) 사이에 위치해서, 내가 바로 서버에 접속하는 대신 프록시가 대신 요청하고, 응답도 프록시가 받아서 나에게 전달합니다.
  • 프록시의 사용 목적
    1. 익명성 보장 (IP 숨기기)
    2. 접근 차단 우회 (지역 제한 콘텐츠 접속 등)
    3. 캐싱 (속도 향상)
    4. 보안 검사 (기업 환경 등에서 웹 필터링)

 

 

웹 프록시 툴 종류

 

툴 이름 설명
Burp Suite 가장 유명한 웹 프록시 도구. 보안 테스트 시 필수. 트래픽 가로채기, 수정, 리플레이 가능.
OWASP ZAP 오픈소스 무료 프록시 툴. 자동 스캐닝 기능 탑재. 초보자에게도 친숙.
Fiddler 윈도우에서 많이 쓰는 HTTP 디버깅 프록시 툴. HTTPS 트래픽도 분석 가능.
Charles Proxy 주로 모바일 앱 트래픽 분석 시 사용. 직관적인 UI 제공.
Mitmproxy 터미널 기반 강력한 프록시. 스크립트화와 자동화에 강점.

 

 

 

프록시 서버

  • 프록시 서버는 앞서 설명한 프록시 역할을 하는 서버 그 자체입니다.
  • 프록시 서버의 종류
    - 포워드 프록시 (Forward Proxy): 내부 사용자가 외부에 접속할 때 사용
    - 리버스 프록시 (Reverse Proxy): 외부 사용자가 내부 서버에 접속할 때, 서버 앞단에서 요청을 받아 처리
    - 오픈 프록시 (Open Proxy): 누구나 사용할 수 있도록 개방된 프록시

 

 

VPN vs Proxy

  • VPN은 강력한 보안과 전체 네트워크 적용을 원할 때 사용합니다.
  • 프록시는 특정 앱만 IP 바꾸거나 빠르게 테스트할 때 사용합니다.
구분 VPN Proxy
암호화 전체 트래픽 암호화 (고급 보안) 대부분 암호화 안 함 (HTTP 프록시 기준)
적용 범위 컴퓨터/디바이스 전체 트래픽 특정 앱 또는 브라우저 트래픽만
속도 상대적으로 느릴 수 있음 (암호화 때문에) 보통 빠름 (경량화되어 있음)
익명성 IP 숨기기 + 트래픽 암호화 IP 숨기기만 (보안성 낮음)
사용 용도 공공 Wi-Fi 보안, 국가 차단 우회 등 가벼운 IP 우회, 테스트 목적 등

 

 


실습

  • 웹프록시 툴 설치하기
    - 블로그에 Brup Suite 설치 방법 작성해두었습니다. 바로가기
 

Burp Suite 설치 방법

Burp Suite 설치 방법입니다. http://portswigger.net/burp 접속합니다. Burp Suite - Application Security Testing Software Get Burp Suite. The class-leading vulnerability scanning, penetration testing, and web app security platform. Try for free to

security-student.tistory.com

 

  • 웹프록시 툴을 이용하여 패킷 변조해보기
    1. 브라우저 프록시 설정
        - Burp의 기본 프록시는 127.0.0.1:8080
        -  브라우저에서 프록시 설정을 Burp로 맞추면 브라우저 트래픽이 Burp로 전달됩니다.
        -  HTTPS 트래픽도 분석하려면 Burp의 CA 인증서를 브라우저에 설치해야 합니다.
    2. Intercept (인터셉트) 켜기
        -  Burp > Proxy > Intercept 탭 > "Intercept is on" 으로 활성화
        -  이제 브라우저 요청이 멈추고 Burp에서 보입니다.
    3. 패킷 수정 (변조)
        -  가로챈 HTTP 요청이나 응답을 자유롭게 수정 가능

1. 위 주소 접속화면이고, POST 요청으로 변경해야 하므로 Brup Suite를 실행합니다.

 

 

2. Brup Suite 실행 후 Intercept off를 클릭하여 Open browser를 클릭해서 브라우저를 접속합니다. 주소를 입력하여 확인 해보면 첫번째 줄에 GET / HTTP/1.1 을 확인할 수 있습니다.

 

 

3. 주소를 입력하여 확인 해보면 첫번째 줄에 GET / HTTP/1.1 을 확인할 수 있습니다.

 

4. GET /HTTP/1.1를 POST / HTTP/1.1로 변경하여 Forward 버튼을 클릭하여 Forward를 진행합니다.

 

5. 아래의 화면을 확인할 수 있습니다.

 

6. POST 요청의 'request' 파라미터를 'get-flag'로 설정해야하므로 요청 들어온 것을 오른쪽 클릭하여 Send to Repater를 클릭 후 상단에 Prpeator를 클릭하여 이동합니다.

 

7. 아래와 같이 화면을 확인할 수 있습니다.

 

8. 'request' 파라미터를 'get-flag'로 설정해야하므로 오른쪽 Inspector의 Request body paramters를 클릭하여 Name : request, Value : get-flag 를 입력하여 저장 후 Send 버튼을 클릭합니다.

 

9. 아래와 같이 Response 가 변경된 것을 확인할 수 있고 Request를 복사하여 Proxy로 이동하여 Request를 변경 후 Forward 버튼을 클릭하여 Forward를 진행합니다.

 

 

10. 아래의 화면을 확인할 수 있습니다.

 

11. User-Agent 헤더를 bot으로 설정하기 위해 위에 설명한 방법으로 다시 진행하여 Prpeator로 이동 후 Request에서 User-Agent 의 값을 bot로 변경하여 Send를 클릭합니다.

 

12. 아래와 같이 Response 가 변경된 것을 확인할 수 있고 Request를 복사하여 Proxy로 이동하여 Request를 변경 후 Forward 버튼을 클릭하여 Forward를 진행합니다.

 

13. 아래의 화면을 확인할 수 있습니다.

 

 

14. API_KEY가 SUp3r_STr0000ng_k3y인 Cookie 설정하기 위해 위에 설명한 방법으로 다시 진행하여 Prpeator로 이동 후 Request에서 Cookie: API_KEY=SUp3r_STr0000ng_k3y 입력하거나 Inspector의 Request cookies에 Name : API_KEY, Value : Up3r_STr0000ng_k3y를 입력 후 Add를 클릭합니다.

 

15. 아래와 같이 Response 가 변경된 것을 확인할 수 있고 Request를 복사하여 Proxy로 이동하여 Request를 변경 후 Forward 버튼을 클릭하여 Forward를 진행합니다.

 

16. SUCCESS를 확인하며 PLAG 값을 확인할 수 있습니다.

패킷(Packet)

  • 패킷네트워크를 통해 데이터를 주고받을 때 사용하는 작은 데이터 단위입니다.
  • 우리가 인터넷에서 보내는 데이터(예: 메시지, 파일, 웹 페이지)는 한 번에 보내지 않고, 일정 크기로 잘라서 패킷이라는 작은 조각들로 나눠 전송하며, 패킷들은 목적지에 도착하면 다시 원래의 데이터로 조립됩니다.

 

 

패킷의 필요성 및 용도

  • 패킷의 필요성
    1. 효율성: 데이터를 작게 나눔으로써 여러 사용자가 네트워크를 동시에 사용할 수 있게 합니다.
    2. 속도 향상: 작은 단위로 보내기 때문에 데이터 일부가 문제가 생겨도 전체를 다시 보낼 필요 없이 해당 패킷만 재전송하면 됩니다.
    3. 유연성: 패킷마다 다른 경로를 사용할 수 있어서 네트워크 혼잡 시 우회가 가능합니다.
    4. 신뢰성 향상: TCP 같은 프로토콜에서는 패킷 순서를 관리하고 오류를 검출하여 신뢰성 있게 데이터를 전달합니다.

  • 패킷의 용도
    1. 인터넷 통신의 기본 단위 (웹 페이지 로딩, 이메일 송수신, 동영상 스트리밍 등)
    2. 네트워크 장비 간 데이터 교환 (라우터, 스위치 등)
    3. 네트워크 분석 및 보안 (침입 탐지, 트래픽 분석 등)

 

 

패킷의 구조

  • 패킷을 간략하게 설명하면 헤더, 데이터(페이로드), 트레일러로 구성됩니다.

    구성 요소 설명
    헤더 (Header) 송신지/수신지 주소, 패킷 번호, 프로토콜 정보 등 전송에 필요한 정보
    데이터 (Payload) 실제 전송하려는 내용 (파일, 메시지, 웹페이지 데이터 등)
    트레일러(Trailer) (옵션) 오류 검출 코드(데이터가 손상되지 않았는지 검사)

 

  • Ethernet 패킷의 구조

    Preamble
    (7byte)
    SFD
    (1 byte)
    Destimation Address
    (6 byte)
    Source Address
    (2 byte)
    Type
    (2 byte)
    DATA
    (46 t0 1500 byte)
    FCS
    (4 byte)

    1. Preamble : 수신자가 동기화할 수 있도록 신호 패턴을 보내는 부분
    2. SFD (Start Frame Delimiter) : 프레임 시작을 알리는 표시
    3. Destination Address : 데이터의 목적지 MAC 주소
    4. Source Address : 데이터를 보내는 출발지 MAC 주소
    5. Type : 프레임 안에 있는 데이터의 프로토콜 종류를 나타냄
    6. Data : 실제 전송할 데이터(상위 계층 정보 포함)
        - ex) IP 패킷, TCP 패킷, UDP 패킷, ICMP 패킷 등
    7. FCS (Frame Check Sequence) : 데이터 오류를 검출하기 위한 검사 코드

  • IP 패킷의 구조

    VER (4 bits) HLEN (4 bits) Service type (8 bits) Total length (16 bits)
    Identification (16 bits) Flags (3 bit) Fragmentation offset (13 bits)
    Time-to-live (8 bits) Protocol (8 bits) Header Checksum (16 bits)
    Source IP Address (32 bits)
    Destination IP Address (32 bits)
    Options + Padding (0 to 40 bytes)

    1. VER: IP의 버전 정보, 0x4일 경우 IPv4를 의미
    2. HLEN: IP헤더의 길이로 이 필드 값에 4를 곱한 값이 실제 헤더의 바이트 길이
    3. Service type: 데이터그램을 어떻게 처리할지를 정의하는 서비스의 유형(최소 지연(Delay), 최대 처리율(MTU), 최대 신뢰성(Reliability), 최소 비용 설정(Cost)), 기본 값은 0
    4. Total length: 헤더를 포함한 데이터그램의 전체 길이를 의미
    5. Identification: 데이터그램이 단편화(Fragmentation)될 때 모든 단편에 이 값이 복사되고, 단편화 된 데이터그램이 생성될 때마다 1씩 증가
    6. Flag: 단편화 여부와 단편화된 조각이 첫 번째 조각인지, 중간 혹은 마지막 조각인지를 알려준다.
        - RF(Reserved Fragment): 아직 사용하지 않으므로 항상 0
        - DF(Don`t Fragment): 1이면 단편화되지 않았음을, 0이면 단편화되었음을 의미한다.
        - MF(More Fragment): 0이면 마지막 단편이거나 유일한 단편이고, 1이면 마지막 단편이 아님을 의미한다.
    7. TTL(Time-to-live): 라우팅 과정에서 라우터를 몇 개 이상 통과하면 해당 패킷을 버릴지를 결정
    8. Protocol: IP계층의 서비스를 사용하는 상위 계층 프로토콜을 정의(1: ICMP, 2: IGMP, 6: TCP. 17: UDP)
    9. Header checksum: 패킷 전달 중 발생할 수 있는 오류 검사를 위해 사용하는 것으로, 송신측에서 체크섬을 계산하여 전송
    10. Source IP address: 송신측 IP 주소
    11. Destination IP address: 수신측 IP 주소
    12. Options + padding: 해당 패킷에 대한 옵션 사항, 옵션 내용이 입력될 경우 그 값이 32배수로 데이터가 마무리되도록 0으로 채운다.
     
  • TCP 패킷의 구조

    Source port address (16 bits) Destination port address  (16 bits)
    Sequence number  (32 bits)
    Acknowledgment number  (32 bits)
    HLEN  (4 bits) Reserved  (6 bits) U
    R
    G
    A
    C
    K
    P
    S
    H
    P
    S
    T
    S
    Y
    N
    F
    I
    N
    Window size  (16 bits)
    Checksum  (16 bits) Urgent pointer  (16 bits)
    Options and padding  (up to 40 bytes)

    1. Source port address: 패킷의 출발지 포트 번호 / 
    Destination port address: 패킷의 목적지 포트 번호
    2. Sequence number: 패킷의 순서 값(순서번호)
    3. Acknowledgment number: 통신 상대의 패킷 순서 값(확인응답번호)
    4. HLEN: TCP헤더를 4바이트 단위의 개수로 나타낸 것
    5. Reserved: 나중에 필요할 때 사용하려고 남겨둔 공간
    6. Window size: 상대방이 반드시 유지해야 하는 바이트 단위의 창 크기
    7. Checksum: 데이터 오류 검출을 위한 값
    8. Urgent pointer: 긴급 플래그 값이 설정되었을 때만 유효하며 세그먼트가 긴급 데이터를 포함하고 있을 때 사용 됨 세그먼트의 데이터 부분에서 마지막 긴급 바이트의 번호를 구하기 위하여 순서 번호에 이 번호가 더해짐
    9. Options and padding: 옵션의 종류와 길이, 데이터를 저장 / 옵션이 32bits가 안되면 나머지 비트를 0으로 채움
 
  • UDP 패킷의 구조

    Source port number (16 bits) Destination port number (16 bits)
    Total length (16 bits) Checksum (16 bits)

    1. 
    Source port number: 패킷의 출발지 포트 번호
    2. Total length: UDP 헤더와 데이터 필드를 포함한 전체 패킷의 길이
    3. Checksum: 데이터 오류 검출을 위한 값
    4. Destination port number: 패킷의 목적지 포트 번호

 


 

와이어샤크(Wireshark)

  • Wireshark네트워크 상의 패킷을 캡처하고 분석할 수 있는 유명한 오픈 소스 도구입니다.
  • Wireshark 특징
    1. 실시간 패킷 캡처 및 분석
    2. 패킷을 상세히 분해해서 볼 수 있음 (헤더, 데이터 등)
    3. 다양한 프로토콜 지원 (TCP/IP, HTTP, DNS 등)
    4. 네트워크 문제 해결, 보안 분석, 교육용으로 활용
  • Wireshark 사용 예시
    1. 네트워크 문제 (지연, 손실 등) 원인 분석
    2. 해킹 시도, 이상 트래픽 탐지
    3. 프로토콜 학습 및 분석
    4. 서버나 클라이언트의 통신 흐름 파악
  • Wireshark 명령어 정리
    분류 필터 명령어 설명
    IP 주소 ip.addr == 192.168.0.1 특정 IP 주소와 관련된 모든 트래픽
    ip.src == 192.168.0.1 출발지 IP가 192.168.0.1 인 패킷
    ip.dst == 192.168.0.1 목적지 IP가 192.168.0.1 인 패킷
    포트 번호 tcp.port == 443 TCP 포트 443 (HTTPS) 사용하는 패킷
    udp.port == 53 UDP 포트 53 (DNS) 사용하는 패킷
    tcp.srcport == 80 출발지 포트가 80 인 TCP 패킷
    tcp.dstport == 443 목적지 포트가 443 인 TCP 패킷
    프로토콜 http HTTP 프로토콜 패킷
    https HTTPS 패킷 (주로 TCP 443 포트)
    dns DNS 쿼리 및 응답 패킷
    tcp TCP 프로토콜 패킷 전체
    udp UDP 프로토콜 패킷 전체
    MAC 주소 eth.addr == 00:11:22:33:44:55 특정 MAC 주소 관련 패킷
    eth.src == 00:11:22:33:44:55 출발지 MAC 주소가 특정 MAC 인 경우
    eth.dst == 00:11:22:33:44:55 목적지 MAC 주소가 특정 MAC 인 경우
    포함된 내용 frame contains "naver" 패킷 내에 "naver" 문자열이 포함된 경우
    AND / OR / NOT ip.addr == 192.168.0.1 && tcp.port == 80 IP가 192.168.0.1 그리고 TCP 포트가 80 인 패킷
    `ip.addr == 192.168.0.1
    !tcp TCP 프로토콜이 아닌 패킷
    에러 / 상태 tcp.analysis.flags TCP 재전송, 중복 ACK 등 문제 있는 TCP 패킷
    icmp ICMP (핑, 에러 메시지) 패킷
     

와이어샤크 실습

  • Wireshark를 사용하여 Naver접속 시 사용된 패킷 필터링하기
    1. Wireshark 실행 후 캡처 시작
    2. Wireshark에서 필터 입력
        - 명령어는 ip.addr 을 사용
        - 네이버 IP 확인 방법은 ping 을 보내서 확인
          개발자 도구(F12)를 켜둔 상태에서 Network 맨위의 www.naver.com 클릭하여 Headers에 Remote Address 확인
    3. 결과 확인
        -  TCP Handshake, TLS Handshake, 실제 데이터 전송까지 확인 가능

 

  • Wireshark를 사용하여 자신의 DNS 서버 정보 확인해보기
    1. Wireshark 실행 후 캡처 시작
    2. Wireshark에서 필터 입력
        - 명령어는 dns 를사용
    3. 결과 확인
        - KT의 DNS인 168.126.63.1 를 사용 중인 것을 확인할 수 있음

 

쿠키(Cookie)

  • 쿠키클라이언트(브라우저)에 저장하는 데이터입니다.
  • 쿠키의 특징
    1. 클라이언트에 저장(서버에 부담 적음)
    2. 유효 기간 설정 가능
    3. 브라우저에 저장되므로 보안에 상대적으로 취약함
    4. 크기 제한 있음(대략 4KB)
  • 쿠키의 동작 흐름
    1. 서버가 클라이언트에 쿠키 저장
    2. 브라우저는 쿠키 저장
    3. 클라이언트가 서버 요청 시 쿠키 자동 전송
    4. 서버는 쿠키를 읽고 처리

 

세션 (Session)

  • 세션서버에 저장하는 사용자 정보입니다.
  • 세션의 특징
    1. 서버에 저장(더 안전)
    2. 서버가 메모리에 저장하므로 서버에 부하 발생할 수 있음
    3. 일정 시간 동안 활동 없으면 세션 만료
    4. 서버에 있기 때문에 민감한 정보 저장 가능함
  • 세션의 동작 흐름
    1. 사용자가 로그인 → 서버가 세션 생성
    2. 서버가 세션 ID를 클라이언트에 전달 (보통 쿠키 사용)
    3. 사용자는 요청할 때마다 세션 ID 전달
    4. 서버는 세션 ID로 사용자 식별

 

쿠키와 세션의 발전 과정

  1. 초창기 웹은 상태가 없는 HTTP 프로토콜
      - HTTP는 무상태(Stateless) 프로토콜
      - 사용자가 서버에 요청할 때마다 "누가 보냈는지" 알 수 없음
      - 서버는 요청을 받을 때마다 매번 새 사용자인지 기존 사용자인지 모름
  2. 쿠키의 등장 (1994년, Netscape)
    -  HTTP의 한계를 보완하기 위해 쿠키 등장
    -  서버가 클라이언트에게 데이터를 저장하게 하고, 이후 요청마다 서버로 자동 전송
    -  사용자가 서버에 여러 번 접근할 때 같은 사용자라는 걸 알 수 있게 됨
    -  장점 : 로그인 유지, 사용자 식별, 맞춤형 서비스 제공
    -  단점 : 민감한 정보는 클라이언트에 저장되므로 보안에 취약
  3. 세션의 등장
    -  쿠키만으로는 민감한 정보를 다루기 어려움
    -  서버에 사용자 정보를 저장하고, 사용자에게는 세션 ID만 전달
    -  세션 ID는 보통 쿠키에 저장하거나 URL 파라미터로 전달
    -  서버가 관리하므로 보안이 강화됨
    -  장점 : 서버에서 관리하므로 보안성 높음
    -  단점 : 서버 메모리를 사용하므로 사용자가 많아지면 부하 발생
  4. 보안 강화를 위한 발전
    쿠키와 세션이 널리 쓰이면서 여러 보안 이슈 등장
      - 세션 하이재킹 (Session Hijacking)
      -  쿠키 탈취 (Cross-Site Scripting - XSS)
      -  CSRF (Cross-Site Request Forgery)
    보안 옵션 등장
      -   HttpOnly: JavaScript 접근 제한
      -   Secure: HTTPS에서만 전송
      -   SameSite: CSRF 방지
      -   세션 관리 기법 발전: 세션 타임아웃, 토큰 갱신, 서버 클러스터링 등
  5. 토큰 기반 인증 (JWT 등)
    - 서버 부하를 줄이고, 분산 시스템에서 인증을 원활히 처리하기 위해 JWT (JSON Web Token) 같은 토큰 기반 인증 방식 등장
    - 서버가 상태를 유지하지 않는 방식 (Stateless)으로 인증 가능
    - 특히 모바일 앱, 마이크로서비스 아키텍처에서 활발히 사용
    - 장점: 서버 메모리 사용 없음, 분산 시스템에 유리
    - 단점: 토큰 탈취 시 위험 (만료 시간 및 보안 처리 필수)

 


JWT (JSON Web Token)

  • JWT는 서버나 클라이언트가 정보를 JSON 형식으로 저장하고 전달하는 토큰 기반 인증 방식입니다.
    - JWT는 사용자가 누구인지 증명하는 데 사용되는 토큰입니다.
    - 토큰 안에 사용자의 정보가 암호화되어 들어 있고, 별도의 세션 저장소 없이 인증할 수 있게 해줍니다.
  • JWT 구조 : JWT는 총 3부분으로 구성됨 (xxxxx.yyyyy.zzzzz)
    1. Header(헤더)
        - 토큰 타입과 해시 알고리즘 정보
    {
      "alg": "HS256",
      "typ": "JWT"
    }

    2. Payload(페이로드)
        - 사용자 정보 (claims) 가 담긴 부분
        -  예: 유저 ID, 권한, 토큰 유효 기간 등
    {
      "sub": "1234567890",
      "name": "Alice",
      "iat": 1712311820
    }

    3. Signature(서명)
        -  위의 내용이 변조되지 않았음을 증명
        -  비밀 키로 생성 (예: HMAC SHA256)

    => 세 부분(1, 2, 3)이 base64로 인코딩되어 하나의 문자열로 만들어짐
  • JWT의 장점
    - Stateless: 서버가 세션을 저장할 필요 없음
    -
    확장성: 서버가 여러 대여도 세션 공유할 필요 없음
    -
    빠름: 토큰만 있으면 빠르게 사용자 검증
    -
    유연성: 모바일/웹/API 어디서나 사용 가능
  • JWT의 단점
    - 토큰 탈취 시 위험: 토큰이 유출되면 악용될 수 있음
    -
    토큰 폐기 어려움: JWT는 자체적으로 만료 전까지 유효. 서버가 토큰을 즉시 무효화하려면 별도 블랙리스트 관리가 필요
    -
    Payload가 암호화된 것은 아님 (Base64로 인코딩된 것이라 누구나 읽을 수 있음, 민감정보 저장 금지)

 


브라우저 저장소

 

저장소 데이터 크기 만료 기간 접근 범위 용도
쿠키 (Cookie) 매우 작음 (~4KB) 설정 가능 서버 & 클라이언트 인증 정보, 세션 식별자 등
Local Storage 크고 (~5MB) 없음 (영구 저장) 클라이언트 전용 사용자 설정, 캐시 데이터 등
Session Storage Local Storage와 유사
(~5MB)
브라우저 탭이 닫히면 삭제 클라이언트 전용 일회성 데이터, 탭 단위 저장
IndexedDB 매우 큼 (수백 MB 이상) 없음 클라이언트 전용 복잡한 구조화된 데이터 저장 (오프라인 앱 등)

 

  1. 쿠키 (Cookie)
    - 서버와 함께 사용하는 유일한 저장소
    - 매 요청마다 자동으로 서버에 전송됨
    - 세션 ID 저장 등 인증 목적으로 많이 사용
    - 용량이 적고, 서버로 전송되므로 주의 필요
    - 보안 옵션: HttpOnly, Secure, SameSite
    document.cookie = "token=abc123; path=/; max-age=3600";
     
  2. Local Storage
    - 브라우저에 데이터 영구 저장
    - 서버로 자동 전송되지 않음 (수동으로 꺼내서 사용해야 함)
    - 브라우저를 꺼도 남아있음
    - 용량이 쿠키보다 훨씬 큼 (~5MB)
    localStorage.setItem("theme", "dark");
    const theme = localStorage.getItem("theme");
     
  3. Session Storage
    - Local Storage와 비슷하지만 세션 단위 저장소
    - 브라우저 탭이나 창이 닫히면 데이터 삭제
    - 다른 탭이나 창과 공유되지 않음 (탭 고유 저장소)
    sessionStorage.setItem("tempData", "12345");
    const tempData = sessionStorage.getItem("tempData");
     
  4. IndexedDB
    - 구조화된 대용량 데이터를 저장하는 비관계형 데이터베이스
    - 오프라인 웹 애플리케이션에서 많이 사용
    - 비동기적으로 작동하고, 키-값 쌍으로 데이터 저장
    - 크기가 수백 MB까지 가능!
    - IndexedDB는 코드가 복잡해서 라이브러리를 주로 사용

 

 

 

+ Recent posts