회원가입 | 고객센터 |
DESIGNONEX
디자인원엑스
About
Service
Q&A
PR리그
자유게시판N
갤러리
포인트게임
공지사항N
통계
로그인 회원가입
고객센터
3.4 라우팅 시스템

URL 처리 방식 • 라우팅 규칙 • 동적 라이팅

A Administrator
2026.04.21 00:51(수정됨) 98 0

1. 개요 — 라이팅 시스템의 구성 원리

DX 미니 프레임워크의 라이팅 시스템(Routing System)은 모든 HTTP 요청을 단일 진입점(index.php)으로 집결시킨 뒤, 내장 Router • Dispatcher • DxRouter 세 계층이 협력하여 요청을 처리하는 구조입니다. PHP 5.6부터 8.x까지, 그리고 Apache • Nginx • IIS • 저가형 웹호스팅까지 단일 코드베이스로 완전 호환됩니다.


핵심 설계 목표:

  • 단일 진입점: 루트의 index.php 만 직접 실행 가능, 나머지 PHP 파일 직접 접근 차단
  • 이중 라우터 체계: 파일 기반 Router(기존) + 클래스 기반 DxRouter(v6.2.0 신규) 병행 운용
  • 서버 독립성: URL Rewrite 모듈 없는 환경에서도 ?_url= 쿼리 파라미터로 자동 폴백
  • 멀티사이트 대응: site_domain 컬럼 기반으로 도메인별 게시판•페이지•테마 분리
 
요청 흐름 개요

HTTP 요청
  └─ .htaccess / nginx.conf / web.config
       └─ index.php (단일 진입점)
            ├─ [STEP 1] 클래스•함수 로드
            ├─ [STEP 2] 보안 초기화 (Secure.php)
            ├─ [STEP 3] DB 연결 + 설정 로드
            ├─ [STEP 4] 플러그인•테마•인증 초기화
            └─ [STEP 5] 라우팅 + 디스패치
                 ├─ DxRouter::dispatch()  ← routes/*.php 에 등록된 라우트 우선
                 └─ Dispatcher(Router)    ← 파일 기반 라우팅 폴백


2. URL 처리 방식


2.1 단일 진입점(index.php) 구조

루트 디렉토리에 위치한 index.php는 DX 프레임워크의 유일한 공개 PHP 파일입니다. .htaccess(Apache) • nginx.conf • web.config(IIS)에서 정적 파일과 install/ 폴더를 제외한 모든 요청을 이 파일로 전달합니다. index.php 자체도 .htaccess의 <Files> 지시문으로만 접근이 허용되며, 나머지 모든 .php 파일은 외부 직접 접근이 차단됩니다.

보안 설계: boards/, controllers/, core/ 등 하위 디렉토리의 PHP 파일은 각 폴더마다 .htaccess로 직접 접근이 차단됩니다.
오직 index.php를 통해서만 모든 기능이 실행됩니다.


2.2 URL Rewrite 동작 방식

각 웹서버별로 URL Rewrite 규칙이 다음과 같이 정의되어 있으며, 모두 동일한 결과(index.php로 전달)를 만들어냅니다.

Apache (.htaccess)
RewriteEngine On
RewriteBase /

# Authorization 헤더 PHP로 전달 (Apache+mod_rewrite 환경 버그 보완)
RewriteCond %{HTTP:Authorization} .
RewriteRule ^ - [E=HTTP_AUTHORIZATION:%{HTTP:Authorization}]

# 정적 파일•폴더는 그대로 서빙
RewriteCond %{REQUEST_FILENAME} -f [OR]
RewriteCond %{REQUEST_FILENAME} -d
RewriteRule ^ - [L]

# sitemap.xml, robots.txt 포함 나머지 전부 → index.php
RewriteRule ^ index.php [QSA,L]

Nginx (nginx.conf.example)
# try_files에서 $uri/(디렉토리 체크)를 제거하는 것이 핵심
# admin/, boards/ 등 실제 폴더가 있어도 index.php로 넘겨야 함
location / {
    try_files $uri /index.php?$query_string;
}

# 루트 index.php만 FastCGI 실행 허용
location = /index.php {
    fastcgi_param SCRIPT_FILENAME $document_root/index.php;
}

# 그 외 .php 파일 직접 실행 차단
location ~ \.php$ { deny all; }

IIS (web.config)
IIS는 URL Rewrite 2.x 모듈이 설치된 경우 web.config의 <rewrite> 블록이 활성화됩니다. 미설치 시에는 해당 블록을 제거하거나 주석 처리하고, ?_url= 폴백 방식을 사용합니다.


2.3 URL Rewrite 미지원 환경 폴백

저가형 웹호스팅 등 URL Rewrite 모듈이 없는 환경을 위해 쿼리 파라미터 방식의 폴백이 구현되어 있습니다. 시스템 설정(url_rewrite=0)이거나 $_GET['_url'] 파라미터가 존재하면 이 방식이 활성화됩니다.
 
구분 URL Rewrite 사용 시 URL Rewrite 미사용 시
게시판 목록 /notice /index.php?_url=/notice
게시물 보기 /notice/view/123 /index.php?_url=/notice/view/123
관리자 /admin/dashboard /index.php?_url=/admin/dashboard
인증 /auth/login /index.php?_url=/auth/login

폴백 URL 생성은 dx_base_url() 함수가 자동으로 처리합니다:
// dx_base_url() — URL Rewrite 여부에 따라 자동 분기
if (dx_config('url_rewrite', '1') === '0') {
    return $base . '/index.php?_url=/' . $path;
}
return $base . '/' . $path;


2.4 URI 정규화 및 세그먼트 파싱

Router::getUri()는 다양한 서버 환경에서 일관된 URI 문자열을 추출하고, Router::parseSegments()는 이를 배열로 분해합니다. 이 과정에서 XSS•경로 순회 공격을 방어합니다.

URI 추출 우선순위
  1. $_GET['_url'] — URL Rewrite 미지원 IIS 환경 폴백 파라미터
  2. $_SERVER['IIS_WasUrlRewritten'] + UNENCODED_URL — IIS URL Rewrite 2.x 환경
  3. dx_request_uri() (= REQUEST_URI) — Apache • Nginx 표준 환경
  4. HTTP_X_REWRITE_URL — ISAPI Rewrite 호환
  5. ORIG_PATH_INFO / PATH_INFO — 구형 호스팅 환경

세그먼트 보안 처리
첫 번째 세그먼트는 sitemap*.xml 또는 robots.txt 형태의 점(.)을 포함한 이름을 허용합니다. 나머지 세그먼트는 영문자•숫자•하이픈•언더스코어만 남기고 나머지 문자를 모두 제거하여 XSS와 경로 순회 공격을 방어합니다.
 
// 세그먼트 정규화 (XSS•경로 순회 방지)
$seg = preg_replace('/[^a-zA-Z0-9\-_]/', '', $seg);

// 이중 슬래시 정규화 (301 리다이렉트)
if (strpos($_dxRawPath, '//') !== false) {
    header('Location: ' . preg_replace('#/+#', '/', $_dxRawPath));
    exit;
}


3. 라우팅 규칙 — Router 클래스

Router 클래스는 core/router/Router.php에 위치하며, URI 세그먼트를 분석하여 요청의 타입과 관련 메타데이터를 결정합니다. 반환된 라우트 배열은 Dispatcher가 받아 실제 처리를 수행합니다.


3.1 라우팅 타입 상수

상수 설명
TYPE_HOME home 루트 경로 / — 홈 페이지
TYPE_PAGE page DB에 등록된 일반 페이지
TYPE_BOARD board 게시판 (목록•조회•작성 등)
TYPE_ADMIN admin 관리자 페이지 (/admin/...)
TYPE_AUTH auth 인증 관련 (/auth/login 등)
TYPE_API api API 엔드포인트, sitemap, robots
TYPE_SEARCH search 통합 검색 (/search)
TYPE_404 404 매칭 실패 — 404 처리


3.2 라우팅 우선순위 및 처리 흐름

Router::parse() 메서드는 다음 순서로 URI를 매칭합니다. 위에서 일치하는 항목이 발견되면 즉시 반환되어 후속 검사를 건너뜁니다.

라우팅 우선순위 (높은 순 → 낮은 순)
[1] 빈 세그먼트         → TYPE_HOME (홈)
[2] /search             → TYPE_SEARCH (통합 검색)
[3] /admin/...          → TYPE_ADMIN (관리자)
[4] /auth/...           → TYPE_AUTH (인증)
[5] /sitemap*.xml       → TYPE_API (사이트맵)
[6] /robots.txt         → TYPE_API (로봇)
[7] /api/...            → TYPE_API (API)
[8] /{board_key}/{action}/{id}
    → DB에서 board_key 조회 성공 시 TYPE_BOARD
[9] /{board_key}        → DB에서 board_key 조회 성공 시 TYPE_BOARD (list 액션)
[10] {전체 경로 슬러그}  → DB에서 slug 조회 성공 시 TYPE_PAGE
[11] {첫 번째 세그먼트}  → DB에서 slug 조회 성공 시 TYPE_PAGE
[12] 매칭 실패           → TYPE_404

게시판 지원 액션 목록:
 
액션 HTTP 메서드 설명
list GET 게시물 목록 조회
view GET 게시물 상세 조회 (/{key}/view/{id})
write GET/POST 게시물 작성 폼 및 저장
edit GET/POST 게시물 수정 폼 및 저장
delete POST 게시물 삭제
reply GET/POST 답글 작성
search GET 게시판 내 검색
bulk POST 일괄 처리 (다중 삭제 등)


3.3 URL 생성 헬퍼

Router 클래스는 URL을 직접 조합하지 않고 헬퍼 메서드를 통해 일관된 URL을 생성하도록 유도합니다.
 
// 페이지 URL 생성
Router::pageUrl('about');         // → /about

// 게시판 URL 생성
Router::boardUrl('notice');         // → /notice (list)
Router::boardUrl('notice', 'view', 123);  // → /notice/view/123

// 관리자 URL 생성
Router::adminUrl('boards');         // → /admin/boards
Router::adminUrl('members', 'edit'); // → /admin/members/edit


3.4 멀티사이트(site_domain) 지원

v6.x 이후부터 boards 테이블과 pages 테이블에 site_domain 컬럼이 추가되었습니다. Router는 information_schema를 통해 컬럼 존재 여부를 자동 감지(캐싱)하며, 컬럼이 있으면 현재 접속 도메인과 비교하여 해당 도메인의 게시판•페이지만 반환합니다. 구버전에서 마이그레이션 전 환경은 컬럼 없이도 정상 동작합니다.
 
// 멀티사이트 게시판 조회 (site_domain 컬럼 자동 감지)
SELECT * FROM `dx_boards`
  WHERE board_key = ?
    AND status = 1
    AND (site_domain = '' OR site_domain = ?)
  LIMIT 1


4. 동적 라이팅 — Dispatcher 클래스

Dispatcher 클래스(core/router/Dispatcher.php)는 Router가 반환한 라우트 배열을 받아 해당 타입에 맞는 핸들러를 실행합니다. 또한 DxExtend의 extend/middle/ 훅을 라우트 확정 직후 실행하여 커스터마이징 포인트를 제공합니다.


4.1 홈 디스패치 (dispatchHome)

루트 경로 / 요청 시 다음 4단계 우선순위로 홈 콘텐츠를 결정합니다.
  1. 현재 테마의 page/home.php — 테마 폴더에 파일이 있으면 최우선 사용
  2. DB에서 현재 도메인의 is_home=1 페이지 — 멀티사이트 도메인 매칭
  3. DB에서 공통(site_domain='') is_home=1 페이지 — 전체 공통 홈
  4. pages/home.php — 최종 폴백 파일

💡 tip: 테마 전용 홈 페이지를 만들려면
   themes/{테마명}/page/home.php 파일을 생성하면
   DB 설정 없이 즉시 적용됩니다.


4.2 페이지 디스패치 (dispatchPage)

DB의 pages 테이블에 등록된 페이지를 처리합니다. page_location 값에 따라 파일 탐색 경로가 달라집니다.
 
page_location 파일 탐색 경로 설명
global pages/{file_path} 사이트 공통 페이지. DX_PAGES/ 기준 경로
theme themes/{테마}/page/{file_path} 테마 전용 페이지. site_domain 테마 우선 사용

is_standalone=1 페이지는 renderStandalone() 으로 처리되어 레이아웃(헤더•푸터) 없이 파일만 직접 실행됩니다. SPA • iframe 콘텐츠 • 외부 서비스 연동 페이지에 활용합니다.

접근 레벨 체계 (access_level):
  • 0 — 전체 공개 (비로그인 포함)
  • 1 — 로그인 회원만 접근 가능
  • 9 — 관리자만 접근 가능


4.3 게시판 디스패치 (dispatchBoard)

게시판 요청은 항상 boards/handler.php 를 통해 처리됩니다. handler.php 내부의 _brd_render() 함수가 DB 쿼리와 변수 준비를 마친 후, DxTheme 폴백 체인으로 스킨 파일을 렌더링합니다.
 
// 게시판 디스패치 흐름
$GLOBALS['dx_board']      = $board;    // 게시판 정보 전역 주입
$GLOBALS['dx_action']     = $action;   // list / view / write ...
$GLOBALS['dx_board_skin'] = $skin;     // 스킨명 (기본: default)
require boards/handler.php;

// handler.php 내부에서 _brd_render($skinAction, $ctx) 호출
// DxTheme::resolveBoardSkin($skin, $action) 으로 파일 해석

접근 레벨 체계 (read_level / write_level):
  • 0 — 전체 공개
  • 1 — 로그인 필요
  • 9 — 관리자 전용

⚠  BIGINT ID 처리: PHP 32비트 환경에서 (int) 캐스팅 시 오버플로우가 발생할 수 있어
   게시물 ID는 ctype_digit() 검증 후 문자열로 유지합니다.


4.4 관리자 디스패치 (dispatchAdmin)

Auth::isAdmin() 검사를 통과하지 못하면 /auth/login 으로 즉시 리다이렉트합니다. 통과 시 admin/index.php 를 로드하며, dx_admin_action 전역 변수로 서브 액션이 전달됩니다.


4.5 인증 디스패치 (dispatchAuth)

core/auth/{action}.php 파일을 로드합니다 (예: login.php, register.php, logout.php). 소셜 로그인 콜백(*_callback 접미사)은 기존 출력 버퍼를 모두 비운 후 독립 HTML 페이지로 렌더링합니다.


4.6 API 디스패치 (dispatchApi)

core/api/{action}.php 파일을 로드합니다. sitemap • robots • captcha_image 는 Content-Type 헤더를 파일에서 직접 설정하며, 나머지는 Dispatcher가 application/json 헤더를 자동 발행합니다.


4.7 레이아웃 렌더링 체계 (renderWithLayout)

콘텐츠 파일과 레이아웃(헤더•푸터) 파일을 분리하여 결합하는 방식입니다. 출력 버퍼링을 활용해 콘텐츠를 $dx_content 변수에 담은 후 레이아웃 파일에 주입합니다.
 
// renderWithLayout 동작 원리
ob_start();
extract($context);       // route, page, type, slug 변수 주입
include $contentFile;    // 콘텐츠 파일 실행
$dx_content = ob_get_clean();

// 레이아웃 파일에서 $dx_content 를 echo 하여 콘텐츠 삽입
require $layoutFile;     // themes/{테마}/layout/main.php

renderPageWithLayout: 페이지 파일 실행 중 에러를 격리하는 강화 버전. PHP notice/warning은 로그에만 기록하고, Exception은 깔끔한 에러 박스로 대체하여 레이아웃을 유지합니다. 이 방식 덕분에 페이지 파일에서 에러가 발생해도 헤더와 푸터는 정상 출력됩니다.


5. DxRouter — 라라벨 스타일 라우터 (v6.2.0+)

v6.2.0에서 추가된 DxRouter 클래스는 routes/ 폴더의 PHP 파일에서 선언적으로 라우트를 등록하고, URI 패턴 매칭•미들웨어•컨트롤러 기반 처리를 지원합니다. 기존 파일 기반 라우팅보다 먼저 실행되며, 매칭되지 않으면 기존 방식으로 폴백됩니다.


5.1 라우트 등록 방식

// routes/web.php 예시

// HTTP 메서드별 등록
DxRouter::get('/mypage/dashboard', 'MemberController@dashboard')
        ->middleware('auth');

DxRouter::post('/api/update', 'MemberController@update')
        ->middleware(['auth', 'csrf']);

// 클로저 라우트
DxRouter::get('/health', function() {
    echo json_encode(['status' => 'ok', 'version' => DX_VERSION]);
    exit;
});

// 그룹 (공통 prefix + middleware)
DxRouter::group(['prefix' => '/shop', 'middleware' => 'auth'], function() {
    DxRouter::get('/cart', 'ShopController@cart');
    DxRouter::post('/order', 'ShopController@order')->middleware('csrf');
});

// REST 리소스 자동 등록
DxRouter::resource('/posts', 'PostController');
// → GET /posts (index), POST /posts (store)
//   GET /posts/{id} (show), PUT /posts/{id} (update)
//   DELETE /posts/{id} (destroy)


5.2 미들웨어 시스템

DxRouter는 내장 미들웨어와 커스텀 미들웨어를 모두 지원합니다.
 
미들웨어 동작
auth 비로그인 시 /auth/login 으로 리다이렉트
admin 비관리자 시 403 응답
guest 로그인 상태면 홈으로 리다이렉트
csrf CSRF 토큰 검증 실패 시 JSON 에러 반환
json Content-Type: application/json 헤더 자동 발행
throttle 요청 제한 (향후 확장용 슬롯)
커스텀 클로저 또는 Class@handle 형태로 등록 가능


5.3 라우트 우선순위와 폴백

index.php [STEP 5] 라우팅 실행 순서

1. routes/*.php 파일 알파벳순 자동 로드
   → DxRouter에 라우트 등록

2. DxRouter::dispatch() 실행
   → 현재 URI + HTTP 메서드 매칭
   → 매칭 성공: 미들웨어 실행 후 컨트롤러/클로저 호출 → 종료
   → 매칭 실패: false 반환

3. 폴백: new Dispatcher(new Router())->dispatch()
   → Router::resolve() 로 URI 분석
   → Dispatcher가 타입별 핸들러 실행


6. DxTheme 폴백 체인

DxTheme::resolve($relPath)는 현재 활성 테마에서 파일을 찾고, 없으면 default 테마에서 다시 탐색합니다. 이 폴백 구조 덕분에 커스텀 테마는 변경이 필요한 파일만 포함하면 됩니다.


파일 탐색 순서 (폴백 체인)

  1. themes/{현재테마}/{relPath}  ← 우선 탐색
  2. themes/default/{relPath}     ← 폴백


게시판 스킨 탐색 순서:

  1. themes/{현재테마}/board_{skin_key}/{action}.php
  2. themes/{현재테마}/board_default/{action}.php
  3. themes/default/board_{skin_key}/{action}.php
  4. themes/default/board_default/{action}.php

💡 site_domain 테마 오버라이드: Dispatcher::loadPageFile()은
   page의 site_domain이 있을 경우 해당 도메인 사이트의 테마를 우선
   사용합니다. 이를 통해 멀티사이트 환경에서 도메인별 완전히
   다른 디자인을 적용할 수 있습니다.


7. 세션 최적화와 보안

index.php는 모든 GET 요청에서 세션을 시작하지 않는 최적화를 적용합니다. 세션 파일 락 경합을 줄여 동시 접속자가 많은 환경에서 응답 속도를 향상시킵니다.

세션 시작 조건 (하나라도 해당되면 세션 시작):
  • POST 요청 또는 Ajax(XHR) 요청
  • 이미 세션 쿠키가 존재하는 경우
  • /admin, /auth 경로
  • /view/, /write, /edit, /reply, /api/ 경로

세션 미시작 조건: 위 조건에 해당하지 않는 순수 GET 요청 (대부분의 게시판 목록•홈 페이지 조회)
 
// DX_SECRET_KEY 기반 동적 키 이름
// 사이트마다 다른 64자리 랜덤 시크릿 키로
// 세션 키 이름 / CSRF 토큰 키 / Rate Limit 키를 동적 생성
// → 소스코드가 공개되어도 키 이름 예측 불가

// 보안 헤더 (Secure.php + .htaccess/web.config 이중 발행)
// X-Frame-Options: SAMEORIGIN
// X-Content-Type-Options: nosniff
// X-XSS-Protection: 1; mode=block
// Referrer-Policy: strict-origin-when-cross-origin


8. 서버 환경별 설정 가이드

환경 URL Rewrite 설정 폴백 방식 비고
Apache .htaccess (mod_rewrite) 자동 (설정 완비) 서브디렉토리 설치 지원
Nginx nginx.conf try_files 자동 (설정 완비) $uri/ 조건 제거 필수
IIS + URL Rewrite web.config <rewrite> 자동 URL Rewrite 2.x 설치 필요
IIS (Rewrite 없음) web.config <rewrite> 블록 삭제 ?_url= 파라미터 저가 호스팅 호환
저가형 호스팅 .htaccess (mod_rewrite 있으면) ?_url= 파라미터 PHP 5.6+ 지원

⚠  Nginx 주의사항: try_files 설정에서 $uri/(디렉토리 체크)를 반드시 제거해야 합니다.
   admin/, boards/ 등 실제 디렉토리가 존재하지만 이 요청들도 index.php로
   전달되어야 프레임워크가 정상 동작합니다.


9. 전체 흐름 요약

아래 다이어그램은 HTTP 요청이 실제 응답까지 처리되는 전체 경로를 보여줍니다.
 
HTTP 요청: GET /notice/view/42

1. Apache .htaccess
   RewriteRule ^ index.php [QSA,L]

2. index.php
   [STEP 1] require core/router/Router.php, Dispatcher.php, DxRouter.php
   [STEP 2] Secure::initSession() → 세션 시작(조건부) → 보안 헤더
   [STEP 3] require data/config.php → DB 연결
   [STEP 4] 플러그인 로드, DxSite, DxTheme, Auth 초기화
   [STEP 5] routes/*.php 자동 로드

3. DxRouter::dispatch()
   → GET /notice/view/42 에 매칭되는 DxRouter 라우트 없음 → false 반환

4. Dispatcher(new Router())->dispatch()
   → Router::resolve()
      getUri()        : /notice/view/42
      parseSegments() : [notice, view, 42]
      parse()         : $seg[0]=notice, $seg[1]=view (boardAction)
                        findBoard('notice') → DB 조회 성공
                        type=BOARD, slug=notice, action=view, id=42

5. DxExtend::runMiddle() — extend/middle/ 커스텀 코드 실행

6. Dispatcher::dispatchBoard()
   checkBoardAccess(board, 'view') → read_level=0 → 통과
   $GLOBALS['dx_board'] = $board
   $GLOBALS['dx_action'] = 'view'
   require boards/handler.php
     → _brd_action_view() → DB에서 게시물 42번 조회
     → _brd_render('view', $ctx)
        → DxTheme::resolveBoardSkin('default', 'view')
           → themes/{현재테마}/board_default/view.php 탐색
           → 없으면 themes/default/board_default/view.php 폴백

7. Dispatcher::renderWithLayout(skinFile, context)
   ob_start() → include skinFile → $dx_content = ob_get_clean()
   require themes/{테마}/layout/main.php  (= 최종 HTML 출력)

8. DxExtend::runBottom() — extend/bottom/ 커스텀 코드 실행
9. ob_end_flush() — 출력 버퍼 최종 전송

댓글0

로그인 후 댓글을 작성할 수 있습니다.
3.4 라우팅 시스템 URL 처리 방식 • 라우팅 규칙 • 동적 라이팅 2026.04.21
30
전체 회원
269
전체 게시글
144
전체 댓글
181
오늘 방문
28,530
전체 방문
1
현재 접속
인기글 7일 이내
최신글
최신댓글
목록