23 KiB
⚡ Test Project: Module A — Speed Test
1. 과제 개요 (Project Overview)
본 과제는 웹 기술 전반에 걸친 실무 숙련도를 빠르고 정확하게 측정하는 Speed Test 형식의 평가입니다. 선수는 A(Design), B(Layout), C(Frontend), D(Backend) 4개 분류에 걸쳐 총 20개의 독립 Task를 제한 시간 내에 완성해야 합니다. 각 Task는 서로 독립적이며, 순서에 상관없이 자유롭게 풀이할 수 있습니다.
2. 기술 스택 및 제약 조건 (Tech Stack & Constraints)
| 분류 | 허용 기술 | 제약 사항 |
|---|---|---|
| A. Design | GIMP | Photoshop 등 타 편집 프로그램 사용 불가 |
| B. Layout | HTML, CSS | JavaScript 사용 불가 |
| C. Frontend | JavaScript (Vanilla) | 외부 라이브러리/프레임워크 사용 불가 |
| D. Backend | PHP, MySQL | PDO 방식으로 DB 연결 |
- 모든 Task의 결과물은 데스크톱 Chrome 브라우저 기준으로 평가합니다.
- 각 Task의 결과물은
/module_a/{task_id}/경로에 저장하십시오. (예:/module_a/a1/,/module_a/b1/) - Design Task 제출 시에는 GIMP 원본 파일(
.xcf)도 함께 포함해야 합니다.
3. Task 목록 및 요구사항 (Tasks)
🎨 A. Design — GIMP 활용 능력
제공 파일: A1(
source.png), A2(base.jpg), A3(photo.jpg,mask.png) Task의 작업 디렉토리에 미리 배치되어 있습니다. A4, A5는 제공 파일이 없으며 선수가 직접 생성합니다.
A1. 레이어 합성 및 텍스트 배치 Easy
GIMP에서 레이어 구조를 활용한 이미지를 제작하십시오.
- 캔버스 크기: 800×400px, 해상도: 72dpi
- 레이어 1:
#2C3E50단색 배경을 채우십시오. - 레이어 2: 제공된 이미지(
source.png)를 불러와 캔버스 중앙에 배치하고 불투명도를 **70%**로 설정하십시오. - 레이어 3: 텍스트 "Hello, GIMP" 를 흰색(
#FFFFFF), 36pt로 추가하고 캔버스 좌측 상단 (x: 20px, y: 20px) 에 배치하십시오. - 모든 레이어를 병합(Flatten Image)하기 전에
.xcf원본 파일로 먼저 저장하십시오. 이후 레이어를 병합하여result.png로 내보내십시오.
A2. 선택 영역 및 레이어 합성 Easy
GIMP의 선택 도구와 레이어를 활용하여 이미지에 도형 효과를 적용하십시오.
- 제공된 이미지(
base.jpg)를 배경 레이어로 여십시오. - 효과 1: 새 레이어를 추가하고 이미지 좌측 상단 (0, 0) ~ (200, 200) 영역을 사각형 선택 도구로 선택한 뒤
#E74C3C으로 채우십시오. 이 레이어의 불투명도를 **60%**로 설정하십시오. - 효과 2: 또 다른 새 레이어를 추가하고 이미지 중앙에 지름 150px 타원 선택 영역을 만드십시오.
Selection → Border(한국어: 선택 → 경계선)에서 경계 두께를 3px로 설정한 뒤#FFFFFF로 채우십시오. .xcf원본 파일로 먼저 저장한 뒤, 레이어를 병합(Flatten Image)하여result.jpg로 내보내십시오.
A3. 색상 보정 및 레이어 마스크 Normal
제공된 이미지(photo.jpg)를 GIMP에서 색상 보정하고 레이어 마스크를 적용하십시오.
- 색상 보정: 색조-채도(Hue-Saturation) 도구로 채도(Saturation)를 +40 올리십시오.
- 곡선 조정: 곡선(Curves) 도구에서 밝은 영역 출력값을 +20 이상, 어두운 영역 출력값을 -20 이하로 조정하여 대비를 높이십시오.
- 레이어 마스크: 이미지 레이어에 레이어 마스크를 추가하고, 제공된 마스크 이미지(
mask.png)를 해당 마스크 레이어에 붙여넣어 피사체 외 배경이 제거되도록 하십시오. - 완성본을
result.png로 내보내기 전에.xcf원본 파일로 먼저 저장하십시오. 내보낼 때 투명 배경이 유지되도록 PNG 형식을 사용하십시오.
A4. 텍스트 이펙트 및 발광 효과 Normal
GIMP에서 텍스트에 발광(glow) 시각 효과를 적용하십시오.
- 캔버스 크기: 600×200px, 배경색:
#1A1A2E - 텍스트 "SPEED TEST" 를 흰색(
#FFFFFF), Bold, 60pt로 캔버스 중앙에 배치하십시오. - 텍스트 레이어를 복사하여 복사본 레이어를 텍스트 레이어 아래에 배치하십시오.
- 복사본 레이어를 래스터화(Layer → Rasterize)한 뒤, 레이어 패널에서 해당 레이어의 모드(Mode)를 **
Color**로 먼저 변경하십시오. 그 다음 전경색을#4A90D9로 설정하고Edit → Fill with Foreground Color를 실행하여 파란색이 적용되도록 하십시오. - 해당 복사본 레이어에 가우시안 흐림(Gaussian Blur) 효과를 반경 8px로 적용하여 외곽 발광 효과를 완성하십시오.
.xcf원본 파일로 먼저 저장한 뒤result.png로 내보내십시오.
A5. 텍스트 워프 및 필터 효과 Hard
GIMP에서 텍스트를 래스터화하고 Distorts 필터를 조합하여 시각적 변형 효과를 구현하십시오.
- 캔버스 크기: 800×300px, 배경색:
#0D0D0D - 텍스트 "DISTORTION" 을 흰색(
#FFFFFF), Bold, 72pt로 캔버스 중앙에 배치하십시오. - 텍스트 레이어를 래스터화(Layer → Rasterize)하십시오.
- 래스터화된 텍스트 레이어에 다음 두 가지 Distorts 필터를 순서대로 적용하십시오.
- Ripple: Amplitude 8, Wavelength 40, 방향 Horizontal
- Whirl and Pinch: Whirl angle 20도, Pinch 0, Radius 1.0
- 필터 적용이 완료된 텍스트 레이어를 복사하여 복사본에 가우시안 흐림(Gaussian Blur) 반경 3px를 적용하고, 복사본을 원본 텍스트 레이어 아래에 배치하여 잔상 효과를 구현하십시오.
.xcf원본 파일로 먼저 저장한 뒤result.png로 내보내십시오.
📐 B. Layout — HTML / CSS 전용
B1. Flexbox 카드 레이아웃 Easy
HTML과 CSS만을 사용하여 카드 목록 레이아웃을 구현하십시오.
- Flexbox를 사용하여 카드를 한 줄에 4개씩 배치하십시오. (
flex-wrap: wrap사용) - 각 카드는 이미지 영역(
<div>, 높이 160px, 배경색 지정), 제목(<h3>), 본문 텍스트(<p>), 버튼(<button>)을 포함해야 합니다. - 카드 간 간격은 24px로 설정하십시오.
- 더미 카드를 8개 작성하십시오. 각 카드의 이미지 영역 배경색은 서로 달라야 합니다.
B2. Sticky 헤더 및 CSS 인터랙션 Easy
HTML과 CSS만을 사용하여 헤더와 CSS 전용 인터랙션을 구현하십시오.
- Flexbox를 사용하여 좌측에 로고 텍스트, 중앙에 메뉴 링크(4개), 우측에 버튼(2개)을 배치하십시오.
- 메뉴 링크 호버 시 하단에 2px 실선 언더라인이
transition과 함께 나타나는 CSS 전용 인터랙션을 구현하십시오. (border-bottom또는::afterpseudo-element 활용) position: sticky; top: 0;을 사용하여 스크롤 시 헤더가 상단에 고정되도록 하십시오.- 헤더 아래에 최소 높이 3000px 이상의 더미 콘텐츠를 배치하여 스크롤이 가능하도록 하십시오.
B3. CSS Grid 2단 레이아웃 Normal
HTML과 CSS만을 사용하여 CSS Grid 기반의 2단 페이지 레이아웃을 구현하십시오.
- CSS Grid를 사용하여 좌측 사이드바(250px 고정)와 우측 메인 콘텐츠 영역(
1fr)으로 구성된 2단 레이아웃을 구현하십시오. - 좌측 사이드바에는 세로 내비게이션 메뉴(5개 항목)를 배치하십시오. 첫 번째 항목은 배경색과 텍스트 색상으로 활성 상태를 표시하십시오.
- 우측 메인 영역에는 제목, 본문 텍스트, 3열 카드 그리드(CSS Grid 사용, 카드 6개)를 순서대로 배치하십시오.
height: 100vh를 전체 레이아웃 래퍼에 적용하고overflow: hidden을 설정하십시오. 사이드바와 메인 영역 각각에height: 100vh와overflow-y: auto를 적용하여 각 영역이 독립적으로 스크롤되도록 하십시오.
B4. CSS 애니메이션 및 트랜지션 Normal
HTML과 CSS만을 사용하여 다양한 CSS 애니메이션과 트랜지션 효과를 하나의 페이지에 구현하십시오.
- 카드 호버 효과: 카드 4개를 Flexbox로 가로 배치하고, 각 카드에 호버 시 위로 8px 이동하고 그림자가 강해지는 트랜지션 효과를 적용하십시오. (
transform: translateY(-8px),box-shadow,transition활용) - 로딩 스피너:
@keyframes를 사용하여 원형 로딩 스피너를 구현하십시오. 스피너는 지름 48px, 테두리 두께 4px이며, 상단 테두리(border-top)만#3498DB색상으로, 나머지 테두리는#e0e0e0으로 표시하고 무한 회전합니다. - 페이드인 텍스트: 페이지 로드 시 제목 텍스트(
<h1>)가translateY(20px)에서translateY(0)으로 이동하며opacity: 0에서opacity: 1로 나타나는@keyframes애니메이션을 구현하십시오. (animation-duration: 0.8s,animation-fill-mode: both)
B5. CSS Grid 및 고급 선택자 활용 Hard
HTML과 CSS만을 사용하여 2단 콘텐츠 레이아웃과 CSS 전용 탭 인터랙션을 구현하십시오.
- CSS Grid를 사용하여 좌측(65%)과 우측(35%)으로 분할된 2단 레이아웃을 구현하십시오.
- 좌측 영역: 높이 300px의 메인 이미지 영역(
<div>, 배경색 지정)과 하단 썸네일 목록(Flexbox,overflow-x: auto가로 스크롤)을 배치하십시오. 썸네일은 5개이며 각각 80×80px<div>로 구현하고 서로 다른 배경색을 지정하십시오. - 우측 영역 상단: 제목, 부제목, 본문 텍스트를 배치하십시오.
- 우측 영역 하단: 탭 3개(개요 / 상세 / 리뷰)와 각 탭의 콘텐츠 영역을 구현하십시오.
- JavaScript 없이
<input type="radio" name="tab">과 CSS:checked선택자를 활용하여 선택된 탭의 콘텐츠만 표시되도록 구현하십시오. <input type="radio">요소는display: none으로 숨기고,<label>요소를 탭 버튼처럼 스타일링하십시오.
- JavaScript 없이
- 우측 영역 최하단에 Primary 버튼과 Secondary 버튼 2개를 나란히 배치하십시오. Primary 버튼에는
@keyframes를 활용하여 호버 시 배경색이 자연스럽게 전환되는 애니메이션을 적용하십시오.
⚡ C. Frontend — JavaScript (Vanilla)
C1. localStorage 기반 메모 앱 Easy
Vanilla JavaScript와 localStorage를 사용하여 간단한 메모 앱을 구현하십시오.
- 텍스트 입력창(
<textarea>)과 "저장" 버튼이 있어야 합니다. - "저장" 버튼 클릭 시 메모가
localStorage의memos키에 JSON 배열로 누적 저장되어야 합니다. 빈 내용은 저장되지 않아야 합니다. - 저장된 메모 목록을 페이지 하단에 렌더링하십시오. 각 항목에는 메모 내용과 "삭제" 버튼이 포함되어야 합니다.
- "삭제" 버튼 클릭 시 해당 항목이
localStorage에서 제거되고 목록이 즉시 갱신되어야 합니다. - 페이지 새로고침 후에도 메모 목록이 유지되어야 합니다.
C2. DOM 조작 및 이벤트 처리 Easy
Vanilla JavaScript를 사용하여 동적 리스트 조작 기능을 구현하십시오.
- 텍스트 입력창(
<input type="text">)과 "추가" 버튼이 있어야 합니다. - "추가" 버튼 클릭 또는 Enter 키 입력 시 입력값이 리스트에 항목으로 추가됩니다. 빈 값은 추가되지 않으며, 추가 후 입력창은 초기화됩니다.
- 각 항목에는 "완료" 버튼과 "삭제" 버튼이 포함됩니다.
- "완료" 버튼 클릭 시 해당 항목의 텍스트에 취소선(
text-decoration: line-through)이 적용됩니다. 다시 클릭 시 원래 상태로 돌아옵니다. - "삭제" 버튼 클릭 시 해당 항목이 즉시 제거됩니다.
- "완료" 버튼 클릭 시 해당 항목의 텍스트에 취소선(
- 리스트 상단에 현재 전체 항목 수와 완료된 항목 수를 실시간으로 표시하십시오.
C3. JSON 데이터 동적 렌더링 및 필터링 Normal
Vanilla JavaScript와 Fetch API를 사용하여 제공된 로컬 JSON 파일을 받아 동적으로 렌더링하는 페이지를 구현하십시오.
- 제공된
posts.json파일(/module_a/c3/posts.json)을 Fetch API로 불러오십시오. 파일에는id,title,body,category키를 가진 객체 배열이 포함되어 있습니다. (총 20개) - 불러오는 동안 로딩 스피너(CSS 애니메이션)를 표시하고, 완료 후 제거하십시오.
- 데이터 전체를 카드 형태로 렌더링하십시오. 각 카드에는
id,title,body,category가 표시되어야 합니다. - 제목(
title)을 기준으로 실시간 필터링이 가능한 검색창을 구현하십시오. (대소문자 구분 없음, 입력할 때마다 즉시 반영) - Fetch 요청 실패 시 "Failed to load data." 오류 메시지를 화면에 표시하십시오.
제공 파일:
posts.json(선수 작업 디렉토리/module_a/c3/에 미리 배치됨)
C4. 폼 유효성 검사 Normal
Vanilla JavaScript를 사용하여 실시간 폼 유효성 검사를 구현하십시오.
- 다음 필드를 포함한 회원 가입 폼을 작성하십시오: 이름, 이메일, 비밀번호, 비밀번호 확인
- 각 필드의 유효성 규칙은 다음과 같습니다.
- 이름: 필수, 2자 이상
- 이메일: 필수, 이메일 형식 (
@와.포함 여부로 검사) - 비밀번호: 필수, 8자 이상, 영문자와 숫자를 모두 포함
- 비밀번호 확인: 필수, 비밀번호 필드와 동일한 값
- 각 필드에서 포커스가 벗어날 때(
blur이벤트) 해당 필드 아래에 오류 메시지를 표시하십시오. 조건을 충족하면 오류 메시지를 즉시 제거하십시오. - "제출" 버튼은 모든 필드가 유효할 때만 활성화됩니다. 클릭 시
alert("Registration complete.")를 표시하십시오.
C5. IntersectionObserver와 지연 렌더링 Hard
IntersectionObserver를 활용하여 무한 스크롤과 지연 렌더링을 구현하십시오.
- 더미 데이터 50개를 JavaScript 배열로 미리 정의하십시오. 각 항목은
id,title,color를 포함하십시오. (color는#RRGGBB형식의 임의 색상값) - 초기 로드 시 아이템을 10개 렌더링하십시오. 각 아이템은 높이 200px의 색상
<div>와title텍스트로 구성합니다. 초기 10개는color값을 배경색으로 바로 표시합니다. - 목록 맨 아래에 sentinel 요소(
<div id="sentinel">)를 두고, 해당 요소가 뷰포트에 진입하면 자동으로 다음 10개를 추가 렌더링하십시오. - 아이템이 추가되는 동안 sentinel 바로 앞에 로딩 스피너 요소를 삽입하여 화면에 표시하고, 렌더링 완료 후 제거하십시오. (비동기 시뮬레이션:
setTimeout600ms 사용) - 총 50개에 도달하면 sentinel 감지를 중단(
observer.unobserve)하고 "All items loaded." 메시지를 표시하십시오. - 새로 추가되는 아이템의 색상
<div>는 배경색을#cccccc(회색)로 설정한 채로 DOM에 추가하십시오. 별도의IntersectionObserver인스턴스를 사용하여 해당<div>가 뷰포트에 진입하는 순간data-color속성에 저장된 실제 색상값을 배경색으로 교체하십시오.
🛢️ D. Backend — PHP / MySQL
제공 파일: D4의
posts_dump.sql과 D5의data_dump.sql은 해당 Task의 작업 디렉토리에 미리 배치되어 있습니다.
D1. PDO 기반 CRUD API Easy
PHP와 MySQL(PDO)을 사용하여 데이터를 처리하는 REST API를 작성하십시오.
- 아래 테이블 구조로
items테이블을 생성하십시오. 테이블 생성 SQL을table.sql로 함께 제출하십시오.id(INT, PK, AUTO_INCREMENT),title(VARCHAR 100, NOT NULL),content(TEXT),created_at(TIMESTAMP DEFAULT CURRENT_TIMESTAMP)
- 다음 4개의 엔드포인트를 단일 파일
api.php에 구현하십시오. HTTP 메서드($_SERVER['REQUEST_METHOD'])로 분기하십시오.GET /module_a/d1/api.php→ 전체 목록을 JSON 배열로 반환POST /module_a/d1/api.php→ 항목 추가. Body(JSON):{"title": "...", "content": "..."}. 요청 본문은php://input으로 읽어json_decode()로 파싱하십시오. 성공 시 삽입된 행을 JSON으로 반환PUT /module_a/d1/api.php?id={id}→title,content수정. Body(JSON):{"title": "...", "content": "..."}. 요청 본문은php://input으로 읽어json_decode()로 파싱하십시오. 성공 시 수정된 행을 JSON으로 반환DELETE /module_a/d1/api.php?id={id}→ 항목 삭제. 성공 시{"message": "deleted"}반환
- 존재하지 않는
id에 대한 PUT/DELETE 요청 시 HTTP 상태코드 404와{"error": "Not found"}를 반환하십시오. - 모든 응답은
Content-Type: application/json헤더와 함께 반환하십시오.
D2. 파일 업로드 처리 Easy
PHP를 사용하여 이미지 파일 업로드 기능을 구현하십시오.
upload.php에 파일 업로드 HTML 폼(GET)과 업로드 처리(POST)를 구현하십시오. 폼의enctype은multipart/form-data로 설정하십시오.- 업로드 허용 조건: 확장자는 jpg, jpeg, png, gif만 허용, 파일 크기는 2MB 이하.
- 조건을 통과한 파일은
uploads/디렉토리에{time()}_{원본파일명}형식으로 저장하십시오.uploads/디렉토리가 없으면 직접 생성하십시오. - 저장 성공 시 업로드된 이미지를
<img>태그로 페이지에 표시하십시오. - 조건 위반 시 오류 메시지를 표시하십시오.
- 확장자 불일치: "File type not allowed."
- 용량 초과: "File size exceeds 2MB."
D3. 세션 기반 인증 시스템 Normal
PHP 세션과 MySQL을 사용하여 회원 가입, 로그인, 로그아웃 기능을 구현하십시오.
users테이블:id(INT, PK, AUTO_INCREMENT),email(VARCHAR 100, UNIQUE),password(VARCHAR 255),name(VARCHAR 50),created_at(TIMESTAMP DEFAULT CURRENT_TIMESTAMP)register.php: GET 요청 시 회원 가입 HTML 폼을 출력합니다. POST 요청 시 이메일 중복을 확인하고,password_hash()로 비밀번호를 해시하여 DB에 저장한 후 별도 메시지 없이 즉시login.php로 리다이렉트합니다. 이메일 중복 시 폼 위에 "This email is already registered." 오류 메시지를 표시합니다.login.php: GET 요청 시 로그인 HTML 폼을 출력합니다. POST 요청 시password_verify()로 검증하고, 성공 시 세션에user_id,name,email을 저장한 후mypage.php로 리다이렉트합니다. 실패 시 폼 위에 "Incorrect email or password." 오류 메시지를 표시합니다.logout.php: GET 요청 시session_destroy()로 세션을 파기하고login.php로 리다이렉트합니다.mypage.php: 세션에user_id가 없으면login.php로 리다이렉트합니다. 로그인 상태에서는 "Welcome, {name}!" 메시지와 로그아웃 링크를 화면에 출력합니다.
D4. 페이지네이션 구현 Normal
PHP와 MySQL(PDO)을 사용하여 서버 사이드 페이지네이션을 구현하십시오.
posts테이블을 직접 생성한 뒤, 제공된 SQL 덤프(posts_dump.sql)를 임포트하십시오.posts테이블 구조:id,title,content,created_atindex.php에서 게시글 목록을 한 페이지에 10개씩 표시하십시오.- 현재 페이지는 URL 쿼리스트링
?page={n}으로 결정되며, 기본값은 1입니다.LIMIT과OFFSET을 사용한 SQL 쿼리로 해당 페이지의 데이터만 조회하십시오.page값이 1 미만이거나 전체 페이지 수를 초과하면 1페이지로 리다이렉트하십시오. - 페이지 하단에 페이지네이션 링크를 표시하십시오. 전체 페이지 번호를 모두 표시하며, 현재 페이지 번호는 활성 스타일로 구분하고, 이전/다음 버튼을 포함하십시오. 첫 페이지에서 이전 버튼, 마지막 페이지에서 다음 버튼은 비활성화하십시오.
- 각 게시글 항목에는 제목과 등록일이 표시되며, 제목 클릭 시
view.php?id={id}로 이동하여 해당 게시글의 전체 내용을 표시하십시오.
D5. 다중 테이블 집계 쿼리 Hard
복수 테이블 JOIN과 집계 쿼리를 활용한 통계 API를 구현하십시오.
- 아래 구조에 맞춰 테이블 3개를 직접 생성한 뒤, 제공된 SQL 덤프(
data_dump.sql)를 임포트하십시오. 테이블 생성 순서는users→posts→comments순서를 지켜야 합니다. (외래 키 참조 순서)users(id,name,created_at)posts(id,user_id— users.id 참조,title,category,view_count,created_at)comments(id,post_id— posts.id 참조,created_at)
- 단일 파일
stats.php에서type쿼리스트링 값으로 분기하여 다음 3개의 응답을 반환하십시오.GET /module_a/d5/stats.php?type=daily→ 최근 7일간 날짜별 신규 게시글 수를 날짜 오름차순으로 반환. 응답 형식:[{"date": "2026-04-06", "count": 5}, ...]GET /module_a/d5/stats.php?type=category→ 카테고리별 총 게시글 수와 평균 조회수를 게시글 수 내림차순으로 반환. 응답 형식:[{"category": "tech", "post_count": 12, "avg_views": 340}, ...]GET /module_a/d5/stats.php?type=top→ 댓글 수 기준 상위 5개 게시글을 댓글 수 내림차순으로 반환. 응답 형식:[{"post_id": 3, "title": "...", "author": "Alice Johnson", "comment_count": 18}, ...]
- 모든 쿼리는 PDO Prepared Statement로 작성하십시오.
- 알 수 없는
type값에 대해 HTTP 400과{"error": "Invalid type"}을 반환하십시오.
4. 제출 안내 (Submission Guidelines)
선수는 작업 결과물을 서버 디렉토리 /module_a/에 업로드하십시오. 각 Task 결과물은 Task ID를 이름으로 한 하위 디렉토리에 저장해야 합니다. (예: /module_a/a1/, /module_a/b1/, /module_a/c1/, /module_a/d1/)
5. 채점 기준 요약 (Marking Scheme — Total 25점)
| 분류 | Task | 배점 |
|---|---|---|
| A. Design | A1. 레이어 합성 및 텍스트 배치 | 0.5 |
| A2. 선택 영역 및 레이어 합성 | 0.5 | |
| A3. 색상 보정 및 레이어 마스크 | 1.5 | |
| A4. 텍스트 이펙트 및 발광 효과 | 1.5 | |
| A5. 텍스트 워프 및 필터 효과 | 2.0 | |
| B. Layout | B1. Flexbox 카드 레이아웃 | 0.5 |
| B2. Sticky 헤더 및 CSS 인터랙션 | 0.5 | |
| B3. CSS Grid 2단 레이아웃 | 1.5 | |
| B4. CSS 애니메이션 및 트랜지션 | 1.5 | |
| B5. CSS Grid 및 고급 선택자 활용 | 2.0 | |
| C. Frontend | C1. localStorage 기반 메모 앱 | 0.5 |
| C2. DOM 조작 및 이벤트 처리 | 0.5 | |
| C3. JSON 데이터 동적 렌더링 및 필터링 | 1.5 | |
| C4. 폼 유효성 검사 | 1.5 | |
| C5. IntersectionObserver와 지연 렌더링 | 2.0 | |
| D. Backend | D1. PDO 기반 CRUD API | 0.5 |
| D2. 파일 업로드 처리 | 0.5 | |
| D3. 세션 기반 인증 시스템 | 1.5 | |
| D4. 페이지네이션 구현 | 1.5 | |
| D5. 다중 테이블 집계 쿼리 | 3.0 | |
| 합계 | 25 |