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>

 

1

+ Recent posts