다중 에이전트로 하우스 스타일(ReactIntroPage) 그대로 12개 강좌 페이지 생성: 앱의 지형 → 안드로이드 구조 → Kotlin → iOS 구조 → Swift → React Native → Flutter → 앱 내비게이션 → 상태·데이터 → PWA → 앱 보안 → 스토어 배포 - 프레이밍: '웹 개발자가 보는 모바일' — 학생 React 지식을 다리로(RN·PWA), 안드로이드·iOS 둘 다 정확히 다루되 인식·literacy 수준 - 각 페이지: hero·pill-nav·6~7섹션·SVG 다이어그램·tip/warn·다음코스 링크, 색은 전부 테마 변수(하드코딩 0), 386~489줄 - courseCatalog에 lazy import 12 + '앱 개발' 카테고리(라우팅·허브·완료바 자동) - 확인 퀴즈 60문항(코스당 5) — quizzes.json 병합 + 운영 DB 적재 - 총 코스 71→83 검증: 빌드 통과, 하우스스타일 일괄 점검(12/12 준수), 운영 E2E — 학습센터 앱개발 카테고리 노출·React Native 렌더·PWA 퀴즈 채점(정답 서버보관)·콘솔에러0. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
232 lines
24 KiB
JavaScript
232 lines
24 KiB
JavaScript
// 이 파일이 하는 일: 학습 센터의 "코스 카탈로그" — 모든 코스의 목록(데이터)과
|
|
// 화면 컴포넌트 연결을 한곳에서 관리한다. 코스를 추가하려면 이 파일에 한 항목만 넣으면 된다.
|
|
// ⚠️ 배열의 순서 = 배우는 순서. 허브 카드의 번호(01, 02...)가 이 순서대로 매겨진다.
|
|
//
|
|
// 학습 포인트 1: 코스가 70개가 되면 App.jsx에 라우트를 일일이 쓰는 방식은 못 버틴다.
|
|
// "데이터(배열) → 화면(map)" 패턴으로 바꾸면 코드는 그대로 두고 데이터만 늘리면 된다.
|
|
// 학습 포인트 2: lazy(지연 로딩) — 사용자가 그 코스를 열 때만 해당 파일을 내려받는다.
|
|
// 빌드 결과(dist/assets)를 보면 코스마다 파일이 쪼개져(chunk) 있는 걸 확인할 수 있다.
|
|
import { lazy } from 'react';
|
|
|
|
// ── 기존 코스 (pages/) ──
|
|
const HardwarePage = lazy(() => import('./pages/HardwarePage'));
|
|
const AssemblyPage = lazy(() => import('./pages/AssemblyPage'));
|
|
const NetworkPage = lazy(() => import('./pages/NetworkPage'));
|
|
const LinuxPage = lazy(() => import('./pages/LinuxPage'));
|
|
const SqlPage = lazy(() => import('./pages/SqlPage'));
|
|
const CodingBasicsPage = lazy(() => import('./pages/CodingBasicsPage'));
|
|
const HtmlCssPage = lazy(() => import('./pages/HtmlCssPage'));
|
|
const JavascriptPage = lazy(() => import('./pages/JavascriptPage'));
|
|
const UiUxPage = lazy(() => import('./pages/UiUxPage'));
|
|
const FigmaPage = lazy(() => import('./pages/FigmaPage'));
|
|
// ── 코스 (pages/courses/) ──
|
|
const OsPage = lazy(() => import('./pages/courses/OsPage'));
|
|
const CpuDeepPage = lazy(() => import('./pages/courses/CpuDeepPage'));
|
|
const MemoryStoragePage = lazy(() => import('./pages/courses/MemoryStoragePage'));
|
|
const FilesPage = lazy(() => import('./pages/courses/FilesPage'));
|
|
const BinaryPage = lazy(() => import('./pages/courses/BinaryPage'));
|
|
const TroubleshootingPage = lazy(() => import('./pages/courses/TroubleshootingPage'));
|
|
const PeripheralsPage = lazy(() => import('./pages/courses/PeripheralsPage'));
|
|
const HistoryPage = lazy(() => import('./pages/courses/HistoryPage'));
|
|
const GpuPage = lazy(() => import('./pages/courses/GpuPage'));
|
|
const MainboardPowerPage = lazy(() => import('./pages/courses/MainboardPowerPage'));
|
|
const IpDeepPage = lazy(() => import('./pages/courses/IpDeepPage'));
|
|
const HomeNetworkPage = lazy(() => import('./pages/courses/HomeNetworkPage'));
|
|
const DnsDeepPage = lazy(() => import('./pages/courses/DnsDeepPage'));
|
|
const HttpPage = lazy(() => import('./pages/courses/HttpPage'));
|
|
const TcpUdpPage = lazy(() => import('./pages/courses/TcpUdpPage'));
|
|
const NetToolsPage = lazy(() => import('./pages/courses/NetToolsPage'));
|
|
const WifiSecurityPage = lazy(() => import('./pages/courses/WifiSecurityPage'));
|
|
const FirewallPage = lazy(() => import('./pages/courses/FirewallPage'));
|
|
const CloudNetworkPage = lazy(() => import('./pages/courses/CloudNetworkPage'));
|
|
const BandwidthPage = lazy(() => import('./pages/courses/BandwidthPage'));
|
|
const VpnProxyPage = lazy(() => import('./pages/courses/VpnProxyPage'));
|
|
const LinuxAdvancedPage = lazy(() => import('./pages/courses/LinuxAdvancedPage'));
|
|
const ServerAnatomyPage = lazy(() => import('./pages/courses/ServerAnatomyPage'));
|
|
const AwsIntroPage = lazy(() => import('./pages/courses/AwsIntroPage'));
|
|
const DockerIntroPage = lazy(() => import('./pages/courses/DockerIntroPage'));
|
|
const SqlIntermediatePage = lazy(() => import('./pages/courses/SqlIntermediatePage'));
|
|
const DataModelingPage = lazy(() => import('./pages/courses/DataModelingPage'));
|
|
const ApiPage = lazy(() => import('./pages/courses/ApiPage'));
|
|
const OpsBasicsPage = lazy(() => import('./pages/courses/OpsBasicsPage'));
|
|
const CacheNosqlPage = lazy(() => import('./pages/courses/CacheNosqlPage'));
|
|
const CicdPage = lazy(() => import('./pages/courses/CicdPage'));
|
|
const GitDeepPage = lazy(() => import('./pages/courses/GitDeepPage'));
|
|
const ReactIntroPage = lazy(() => import('./pages/courses/ReactIntroPage'));
|
|
const JavaBasicsPage = lazy(() => import('./pages/courses/JavaBasicsPage'));
|
|
const SpringIntroPage = lazy(() => import('./pages/courses/SpringIntroPage'));
|
|
const DataStructuresPage = lazy(() => import('./pages/courses/DataStructuresPage'));
|
|
const DebuggingPage = lazy(() => import('./pages/courses/DebuggingPage'));
|
|
const CleanCodePage = lazy(() => import('./pages/courses/CleanCodePage'));
|
|
const TestingPage = lazy(() => import('./pages/courses/TestingPage'));
|
|
const AiToolsPage = lazy(() => import('./pages/courses/AiToolsPage'));
|
|
const ColorPage = lazy(() => import('./pages/courses/ColorPage'));
|
|
const TypographyPage = lazy(() => import('./pages/courses/TypographyPage'));
|
|
const LayoutPage = lazy(() => import('./pages/courses/LayoutPage'));
|
|
const IconsImagesPage = lazy(() => import('./pages/courses/IconsImagesPage'));
|
|
const DesignSystemPage = lazy(() => import('./pages/courses/DesignSystemPage'));
|
|
const MobileDesignPage = lazy(() => import('./pages/courses/MobileDesignPage'));
|
|
const UserResearchPage = lazy(() => import('./pages/courses/UserResearchPage'));
|
|
const PortfolioPage = lazy(() => import('./pages/courses/PortfolioPage'));
|
|
const WireframePage = lazy(() => import('./pages/courses/WireframePage'));
|
|
const DesignRefsPage = lazy(() => import('./pages/courses/DesignRefsPage'));
|
|
const ToolDockerPage = lazy(() => import('./pages/courses/ToolDockerPage'));
|
|
const ToolDbeaverPage = lazy(() => import('./pages/courses/ToolDbeaverPage'));
|
|
const ToolVscodePage = lazy(() => import('./pages/courses/ToolVscodePage'));
|
|
const ToolIntellijPage = lazy(() => import('./pages/courses/ToolIntellijPage'));
|
|
const ToolPostmanPage = lazy(() => import('./pages/courses/ToolPostmanPage'));
|
|
const ToolDevtoolsPage = lazy(() => import('./pages/courses/ToolDevtoolsPage'));
|
|
const ToolTerminalPage = lazy(() => import('./pages/courses/ToolTerminalPage'));
|
|
const ToolGiteaPage = lazy(() => import('./pages/courses/ToolGiteaPage'));
|
|
const ToolCapturePage = lazy(() => import('./pages/courses/ToolCapturePage'));
|
|
const ToolNotionPage = lazy(() => import('./pages/courses/ToolNotionPage'));
|
|
const ToolShortcutsPage = lazy(() => import('./pages/courses/ToolShortcutsPage'));
|
|
|
|
// ── 앱 개발 카테고리 (안드로이드·iOS·크로스플랫폼) ──
|
|
const AppLandscapePage = lazy(() => import('./pages/courses/AppLandscapePage'));
|
|
const AndroidBasicsPage = lazy(() => import('./pages/courses/AndroidBasicsPage'));
|
|
const KotlinIntroPage = lazy(() => import('./pages/courses/KotlinIntroPage'));
|
|
const IosBasicsPage = lazy(() => import('./pages/courses/IosBasicsPage'));
|
|
const SwiftIntroPage = lazy(() => import('./pages/courses/SwiftIntroPage'));
|
|
const ReactNativePage = lazy(() => import('./pages/courses/ReactNativePage'));
|
|
const FlutterPage = lazy(() => import('./pages/courses/FlutterPage'));
|
|
const AppNavigationPage = lazy(() => import('./pages/courses/AppNavigationPage'));
|
|
const AppStateDataPage = lazy(() => import('./pages/courses/AppStateDataPage'));
|
|
const PwaPage = lazy(() => import('./pages/courses/PwaPage'));
|
|
const AppSecurityPage = lazy(() => import('./pages/courses/AppSecurityPage'));
|
|
const AppStoreDeployPage = lazy(() => import('./pages/courses/AppStoreDeployPage'));
|
|
|
|
// 카탈로그 본체. slug = /learn/<slug> 주소. 배열 순서 = 배우는 순서(카드에 번호로 표시).
|
|
export const COURSE_CATEGORIES = [
|
|
{
|
|
category: '컴퓨터 기초',
|
|
desc: '소프트웨어 이전에, 기계 그 자체를 이해해요.',
|
|
items: [
|
|
{ slug: 'history', icon: '📜', title: '컴퓨터의 역사', desc: '에니악에서 AI까지 — 우리가 어디쯤 서 있는지부터.', level: '교양', Component: HistoryPage },
|
|
{ slug: 'hardware', icon: '💻', title: '컴퓨터의 구성', desc: 'CPU·RAM·스토리지·메인보드 — 부품의 역할과 스펙 읽기.', level: '입문', Component: HardwarePage },
|
|
{ slug: 'binary', icon: '🔢', title: '2진수와 데이터 표현', desc: '문자·색·이미지가 전부 숫자인 이유.', level: '입문', Component: BinaryPage },
|
|
{ slug: 'cpu-deep', icon: '🧠', title: 'CPU 깊이 보기', desc: '명령어 사이클·코어·캐시·발열 — 두뇌의 작동 원리.', level: '심화', Component: CpuDeepPage },
|
|
{ slug: 'memory-storage', icon: '💾', title: '메모리와 저장장치 깊이 보기', desc: 'RAM·SSD의 내부, 스왑과 느려짐의 연쇄.', level: '심화', Component: MemoryStoragePage },
|
|
{ slug: 'gpu', icon: '🎮', title: '그래픽카드와 GPU의 세계', desc: '화면 그리기 전담 — AI 시대에 GPU가 금값인 이유.', level: '심화', Component: GpuPage },
|
|
{ slug: 'mainboard-power', icon: '🔋', title: '메인보드와 파워 깊이 보기', desc: '모든 부품이 만나는 도로망, 그리고 전원의 심장.', level: '심화', Component: MainboardPowerPage },
|
|
{ slug: 'assembly', icon: '🔧', title: '데스크탑 조립하기', desc: '배운 부품들을 내 손으로 — 9단계 조립 순서.', level: '실습', Component: AssemblyPage },
|
|
{ slug: 'os', icon: '🪟', title: '운영체제의 이해', desc: '윈도우·맥·리눅스가 하는 일 — 자원 관리부터 멀티태스킹까지.', level: '입문', Component: OsPage },
|
|
{ slug: 'files', icon: '📁', title: '파일과 폴더의 원리', desc: '확장자의 진실, 경로, 휴지통과 완전삭제.', level: '입문', Component: FilesPage },
|
|
{ slug: 'peripherals', icon: '🖥️', title: '노트북과 주변기기 고르기', desc: '노트북·모니터·키보드 스펙 읽기 — 개발자 관점.', level: '입문', Component: PeripheralsPage },
|
|
{ slug: 'troubleshooting', icon: '🩺', title: '컴퓨터 문제 해결', desc: '느려질 때 점검 순서, 백업 3-2-1, 검색의 기술.', level: '실습', Component: TroubleshootingPage },
|
|
],
|
|
},
|
|
{
|
|
category: '네트워크',
|
|
desc: '랜선 한 가닥에서 클라우드까지.',
|
|
items: [
|
|
{ slug: 'network', icon: '🌐', title: '네트워크의 이해', desc: '랜선(UTP) 구조 → 인터넷의 구조 → DNS·TCP/IP 한 흐름.', level: '입문', Component: NetworkPage },
|
|
{ slug: 'bandwidth', icon: '🚗', title: '네트워크 속도와 대역폭', desc: 'Mbps의 진실 — 다운로드가 생각보다 느린 이유.', level: '입문', Component: BandwidthPage },
|
|
{ slug: 'ip-deep', icon: '🔍', title: 'IP 주소 깊이 보기', desc: '사설 대역·서브넷·DHCP·IPv6 — 주소 체계의 모든 것.', level: '심화', Component: IpDeepPage },
|
|
{ slug: 'home-network', icon: '📡', title: '공유기와 홈 네트워크 실전', desc: '관리자 페이지, WiFi 채널, 포트포워딩까지.', level: '실습', Component: HomeNetworkPage },
|
|
{ slug: 'dns-deep', icon: '📖', title: 'DNS 깊이 보기', desc: '재귀 조회의 여행, 레코드 타입, TTL과 전파.', level: '심화', Component: DnsDeepPage },
|
|
{ slug: 'tcp-udp', icon: '🤝', title: 'TCP와 UDP', desc: '3-way 핸드셰이크, 패킷, 왜 게임은 UDP인가.', level: '심화', Component: TcpUdpPage },
|
|
{ slug: 'http', icon: '📨', title: 'HTTP와 HTTPS', desc: '메서드·상태코드·쿠키(우리 로그인!)·TLS 인증서.', level: '입문', Component: HttpPage },
|
|
{ slug: 'net-tools', icon: '🧰', title: '네트워크 명령어 실전', desc: 'ping·tracert·nslookup·curl — 진단의 무기들.', level: '실습', Component: NetToolsPage },
|
|
{ slug: 'wifi-security', icon: '🔐', title: '무선 네트워크와 보안', desc: '공용 와이파이의 위험, 피싱 구별, 2단계 인증.', level: '입문', Component: WifiSecurityPage },
|
|
{ slug: 'vpn-proxy', icon: '🚇', title: 'VPN과 프록시', desc: '암호화 터널과 대리인 — 우리 Caddy가 실물 교재.', level: '심화', Component: VpnProxyPage },
|
|
{ slug: 'firewall', icon: '🛡️', title: '방화벽과 보안 기초', desc: '포트의 여닫음, 우리 AWS 보안그룹 실례, 최소 권한.', level: '입문', Component: FirewallPage },
|
|
{ slug: 'cloud-network', icon: '☁️', title: '클라우드 네트워크 입문', desc: '리전·VPC·탄력적 IP — 우리 플랫폼의 실제 구성도.', level: '심화', Component: CloudNetworkPage },
|
|
],
|
|
},
|
|
{
|
|
category: '서버와 데이터',
|
|
desc: '백엔드 개발자가 매일 쓰는 도구들.',
|
|
items: [
|
|
{ slug: 'server-anatomy', icon: '🏗️', title: '서버란 무엇인가', desc: '웹서버·WAS·DB 3계층 — 우리 구성 그대로 해부.', level: '입문', Component: ServerAnatomyPage },
|
|
{ slug: 'linux', icon: '🐧', title: '리눅스 / 터미널 기초', desc: '터미널 명령어부터 SSH 서버 접속까지.', level: '입문', Component: LinuxPage },
|
|
{ slug: 'linux-advanced', icon: '⚙️', title: '리눅스 심화', desc: '셸 스크립트, 크론, 파이프 — 자동화의 시작.', level: '심화', Component: LinuxAdvancedPage },
|
|
{ slug: 'docker-intro', icon: '🐳', title: '도커 입문', desc: '컨테이너 개념과 우리 docker-compose.yml 한 줄씩 해부.', level: '입문', Component: DockerIntroPage },
|
|
{ slug: 'aws-intro', icon: '🌩️', title: '클라우드와 AWS 입문', desc: 'EC2·요금·리전 — 우리 인프라가 교재.', level: '입문', Component: AwsIntroPage },
|
|
{ slug: 'sql', icon: '🗄️', title: 'SQL 입문', desc: '우리 플랫폼의 진짜 테이블로 SELECT부터 JOIN까지.', level: '입문', Component: SqlPage },
|
|
{ slug: 'sql-intermediate', icon: '📊', title: 'SQL 중급', desc: 'GROUP BY·집계·인덱스 — 데이터에서 답을 꺼내기.', level: '심화', Component: SqlIntermediatePage },
|
|
{ slug: 'data-modeling', icon: '🗂️', title: '데이터 모델링 기초', desc: 'ERD·키·정규화 — 테이블 설계는 정리정돈.', level: '심화', Component: DataModelingPage },
|
|
{ slug: 'cache-nosql', icon: '⚡', title: '캐시와 NoSQL 맛보기', desc: '자주 쓰는 건 책상 위에 — Redis가 빠른 이유.', level: '심화', Component: CacheNosqlPage },
|
|
{ slug: 'api', icon: '🔌', title: 'API의 이해', desc: 'REST·JSON — 이미 다 써본 우리 API로 배우기.', level: '입문', Component: ApiPage },
|
|
{ slug: 'cicd', icon: '🔁', title: 'CI/CD와 배포 자동화 입문', desc: '푸시하면 알아서 검사·배포 — 파이프라인의 세계.', level: '심화', Component: CicdPage },
|
|
{ slug: 'ops-basics', icon: '🚨', title: '운영과 장애 대응 기초', desc: '장애의 신호, 로그 읽기, 재시작의 정석.', level: '실습', Component: OpsBasicsPage },
|
|
],
|
|
},
|
|
{
|
|
category: '소프트웨어',
|
|
desc: '코드를 읽고, 쓰고, 함께 다듬는 법.',
|
|
items: [
|
|
{ slug: 'coding', icon: '⌨️', title: '코딩 기초', desc: '변수·함수·조건·반복을 이 플랫폼의 실제 코드로.', level: '입문', Component: CodingBasicsPage },
|
|
{ slug: 'htmlcss', icon: '🧱', title: 'HTML / CSS 기초', desc: '웹의 뼈대와 옷 — 박스 모델·플렉스를 우리 스타일로.', level: '입문', Component: HtmlCssPage },
|
|
{ slug: 'javascript', icon: '⚡', title: '자바스크립트 기초', desc: 'F12 콘솔이 실습장 — 변수에서 fetch까지.', level: '입문', Component: JavascriptPage },
|
|
{ slug: 'data-structures', icon: '🧺', title: '자료구조 맛보기', desc: '배열·스택·큐·해시 — 그릇마다 쓰임이 다르다.', level: '입문', Component: DataStructuresPage },
|
|
{ slug: 'react-intro', icon: '⚛️', title: 'React 입문', desc: '컴포넌트·props·state — 우리 프론트가 교재.', level: '입문', Component: ReactIntroPage },
|
|
{ slug: 'java-basics', icon: '☕', title: 'Java 기초', desc: '타입·클래스·객체 — Spring으로 가는 첫걸음.', level: '입문', Component: JavaBasicsPage },
|
|
{ slug: 'spring-intro', icon: '🍃', title: 'Spring Boot 입문', desc: 'DI·어노테이션·계층 구조 — 우리 백엔드 여행.', level: '심화', Component: SpringIntroPage },
|
|
{ slug: 'git-deep', icon: '🌳', title: 'Git 깊이 보기', desc: '브랜치·충돌 해결·되돌리기 3형제·PR 매너.', level: '심화', Component: GitDeepPage },
|
|
{ slug: 'debugging', icon: '🐞', title: '디버깅과 에러 읽는 법', desc: '스택트레이스·브레이크포인트 — 에러는 단서다.', level: '실습', Component: DebuggingPage },
|
|
{ slug: 'testing', icon: '🧪', title: '테스트 코드 입문', desc: '미래의 나를 지키는 보험 — 우리 테스트가 교재.', level: '심화', Component: TestingPage },
|
|
{ slug: 'clean-code', icon: '✨', title: '클린 코드와 코드리뷰', desc: '이름 짓기·작은 함수·리뷰 매너.', level: '심화', Component: CleanCodePage },
|
|
{ slug: 'ai-tools', icon: '🤖', title: 'AI 도구와 함께 코딩하기', desc: '잘 시키고, 검증하고, 배움에 쓰는 법 — 보안 수칙까지.', level: '실습', Component: AiToolsPage },
|
|
],
|
|
},
|
|
{
|
|
category: '디자인',
|
|
desc: '보기 좋은 것을 넘어, 쓰기 좋은 것을.',
|
|
items: [
|
|
{ slug: 'uiux', icon: '🎨', title: 'UI/UX 기초', desc: '좋은 화면의 원칙들 — 우리 플랫폼을 뜯어보며.', level: '입문', Component: UiUxPage },
|
|
{ slug: 'color', icon: '🌈', title: '색 이론 기초', desc: '색상환·배색·우리 브랜드 블루의 비밀.', level: '입문', Component: ColorPage },
|
|
{ slug: 'typography', icon: '🔤', title: '타이포그래피', desc: '폰트·크기 위계·행간 — 가독성의 과학.', level: '입문', Component: TypographyPage },
|
|
{ slug: 'layout', icon: '📐', title: '레이아웃과 그리드', desc: '8pt 그리드·정렬·여백 — 보이지 않는 뼈대.', level: '입문', Component: LayoutPage },
|
|
{ slug: 'icons-images', icon: '🖼️', title: '아이콘과 이미지', desc: 'SVG vs PNG, 해상도, 그리고 저작권!', level: '입문', Component: IconsImagesPage },
|
|
{ slug: 'wireframe', icon: '✏️', title: '와이어프레임 기초', desc: '색 없이 구조만 — 빨리 그리고 빨리 버리는 설계.', level: '실습', Component: WireframePage },
|
|
{ slug: 'figma', icon: '🖌️', title: '피그마와 디자인 툴', desc: '프레임·오토레이아웃·컴포넌트·핸드오프.', level: '실습', Component: FigmaPage },
|
|
{ slug: 'design-system', icon: '🧩', title: '디자인 시스템 입문', desc: '토큰과 컴포넌트 — 우리 global.css가 실물.', level: '심화', Component: DesignSystemPage },
|
|
{ slug: 'mobile-design', icon: '📱', title: '모바일 디자인', desc: '엄지의 법칙, 터치 타깃, 웹 vs 앱.', level: '입문', Component: MobileDesignPage },
|
|
{ slug: 'user-research', icon: '🔬', title: '사용자 리서치 기초', desc: '인터뷰·관찰·사용성 테스트 — 감이 아니라 근거로.', level: '심화', Component: UserResearchPage },
|
|
{ slug: 'design-refs', icon: '🗃️', title: '레퍼런스와 무드보드', desc: '많이 본 사람이 잘 만든다 — 수집과 분석의 루틴.', level: '실습', Component: DesignRefsPage },
|
|
{ slug: 'portfolio', icon: '💼', title: '포트폴리오 만들기', desc: '수습 8주가 곧 재료 — 과정을 이야기로.', level: '실습', Component: PortfolioPage },
|
|
],
|
|
},
|
|
{
|
|
category: '부록 · 도구',
|
|
desc: '손에 익으면 두 배 빨라지는 도구들.',
|
|
items: [
|
|
{ slug: null, external: '/setup', icon: '🛠️', title: '개발 환경 설치 가이드', desc: 'JDK·PATH·Node·Git·IntelliJ — 환경 구축 9단계.', level: '실습' },
|
|
{ slug: 'tool-shortcuts', icon: '⌨️', title: '윈도우 단축키 마스터', desc: '마우스를 버릴수록 빨라진다 — 필수 30개.', level: '실습', Component: ToolShortcutsPage },
|
|
{ slug: 'tool-terminal', icon: '🖤', title: '윈도우 터미널과 PowerShell', desc: 'cmd·PowerShell·Git Bash 삼형제 정리.', level: '실습', Component: ToolTerminalPage },
|
|
{ slug: 'tool-vscode', icon: '📝', title: 'VS Code 200% 활용', desc: '단축키 15개, 확장 추천, 검색의 기술.', level: '실습', Component: ToolVscodePage },
|
|
{ slug: 'tool-intellij', icon: '💡', title: 'IntelliJ 꿀팁 모음', desc: 'Shift 두 번, Alt+Enter, 로컬 히스토리의 구원.', level: '실습', Component: ToolIntellijPage },
|
|
{ slug: 'tool-devtools', icon: '🔎', title: '크롬 개발자도구', desc: 'F12의 세계 — Elements·Console·Network 투어.', level: '실습', Component: ToolDevtoolsPage },
|
|
{ slug: 'tool-gitea', icon: '🍵', title: 'Gitea 200% 활용', desc: '우리 Git 서버 — 이슈·PR·리뷰 화면 사용법.', level: '실습', Component: ToolGiteaPage },
|
|
{ slug: 'tool-docker', icon: '🐳', title: 'Docker Desktop 사용법', desc: '대시보드 읽기, 컨테이너 GUI 관리, 용량 정리.', level: '실습', Component: ToolDockerPage },
|
|
{ slug: 'tool-dbeaver', icon: '🦫', title: 'DBeaver로 DB 들여다보기', desc: '우리 DB 연결, ERD 자동 생성, GUI로 쿼리.', level: '실습', Component: ToolDbeaverPage },
|
|
{ slug: 'tool-postman', icon: '🚀', title: 'Postman으로 API 테스트', desc: '화면 없이 API를 직접 두드리는 법.', level: '실습', Component: ToolPostmanPage },
|
|
{ slug: 'tool-capture', icon: '📸', title: '캡처와 화면 녹화', desc: 'PR·버그 리포트의 언어 — Win+Shift+S부터.', level: '실습', Component: ToolCapturePage },
|
|
{ slug: 'tool-notion', icon: '🗒️', title: '노션으로 기록하기', desc: 'TIL·트러블슈팅 — 검색되는 기록이 자산.', level: '실습', Component: ToolNotionPage },
|
|
],
|
|
},
|
|
{
|
|
category: '앱 개발',
|
|
desc: '웹 개발자가 보는 모바일 — 안드로이드·iOS부터 우리 React로 앱까지.',
|
|
items: [
|
|
{ slug: 'app-landscape', icon: '📱', title: '앱의 지형', desc: '네이티브·크로스플랫폼·웹 — 뭘 언제 고르나.', level: '교양', Component: AppLandscapePage },
|
|
{ slug: 'android-basics', icon: '🤖', title: '안드로이드 앱의 구조', desc: '액티비티·생명주기·스튜디오 — 앱이 도는 원리.', level: '입문', Component: AndroidBasicsPage },
|
|
{ slug: 'kotlin-intro', icon: '🟣', title: 'Kotlin 입문', desc: '안드로이드의 언어 — JS/Java 관점으로 빠르게.', level: '입문', Component: KotlinIntroPage },
|
|
{ slug: 'ios-basics', icon: '🍎', title: 'iOS 앱의 구조', desc: '뷰·생명주기·Xcode — 애플 생태계의 규칙.', level: '입문', Component: IosBasicsPage },
|
|
{ slug: 'swift-intro', icon: '🐦', title: 'Swift 입문', desc: 'iOS의 언어 — 옵셔널·구조체 핵심만.', level: '입문', Component: SwiftIntroPage },
|
|
{ slug: 'react-native', icon: '⚛️', title: 'React Native', desc: '우리 React 지식이 그대로 앱이 되는 다리.', level: '심화', Component: ReactNativePage },
|
|
{ slug: 'flutter', icon: '🦋', title: 'Flutter', desc: 'Dart·위젯 — 또 하나의 크로스플랫폼.', level: '심화', Component: FlutterPage },
|
|
{ slug: 'app-navigation', icon: '🧭', title: '앱 화면과 이동', desc: '내비게이션·화면 전환 — 웹 라우팅과 비교.', level: '입문', Component: AppNavigationPage },
|
|
{ slug: 'app-state-data', icon: '🗃️', title: '앱의 상태와 데이터', desc: '상태관리·로컬 저장·우리 Spring API 연동.', level: '심화', Component: AppStateDataPage },
|
|
{ slug: 'pwa', icon: '🌐', title: 'PWA — 웹앱을 앱처럼', desc: '설치·오프라인·푸시 — 우리 웹앱에 가장 가까운 길.', level: '심화', Component: PwaPage },
|
|
{ slug: 'app-security', icon: '🔐', title: '앱 보안과 권한', desc: '권한 모델·저장소 보안·심사 기준.', level: '입문', Component: AppSecurityPage },
|
|
{ slug: 'app-store-deploy', icon: '🚀', title: '스토어 배포', desc: 'Play·App Store 등록과 심사 흐름.', level: '실습', Component: AppStoreDeployPage },
|
|
],
|
|
},
|
|
];
|
|
|
|
// 라우팅용: slug가 있는 모든 코스를 평평한 배열로.
|
|
// 학습 포인트: flatMap = "각 카테고리의 items를 꺼내 한 배열로 합치기".
|
|
export const ALL_COURSES = COURSE_CATEGORIES.flatMap((c) => c.items.filter((i) => i.slug));
|