· nambak80 Blog 로그인
CodeIgniter4

CodeIgniter 4로 멀티보드 만들기 #2 — 프로젝트 세팅과 이 강좌의 규칙

CodeIgniter 4로 멀티보드 만들기 #2 — 프로젝트 세팅과 이 강좌의 규칙

지난 편에서 우리는 세 갈래 길 중 하나를 골랐습니다. 게시판마다 테이블을 만들지 않고, posts 테이블 하나에 전부 담되 게시판별로 다른 항목은 JSON 컬럼에 넣기로 했습니다.

이번 편에서는 그 결정을 담을 빈 그릇을 만듭니다. CodeIgniter 4를 설치하고, 데이터베이스를 연결하고, 앞으로 37편 동안 지킬 규칙 몇 가지를 정합니다.

설치 자체는 지난 강좌에서 해보셨을 테니 빠르게 지나가겠습니다. 대신 .env에 한 줄을 더 적어야 하는 이유에서는 잠깐 멈추겠습니다. 이 한 줄을 빠뜨리면 열댓 편쯤 뒤에 이유를 알 수 없는 에러를 만나게 됩니다.


프로젝트 만들기

composer create-project codeigniter4/appstarter ci4board
cd ci4board

버전을 확인합니다.

php spark --version
CodeIgniter v4.7.4 Command Line Tool - Server Time: 2026-08-11 06:24:53 UTC+00:00

이 강좌는 4.7.x 를 기준으로 씁니다. 4.5 이상이면 대부분 그대로 따라오실 수 있지만, 나중에 쓸 모델의 $casts 기능이 4.5.0부터 들어왔기 때문에 그보다 낮은 버전은 권하지 않습니다.

PHP는 8.2 이상이 필요하고 intl, mbstring 확장이 있어야 합니다.

php -v
php -m | grep -E 'intl|mbstring'

2. .env 만들기

CodeIgniter는 env 라는 예시 파일을 함께 줍니다. 복사해서 .env 로 만듭니다.

cp env .env

원본 파일은 거의 모든 줄이 # 으로 주석 처리되어 있습니다. 필요한 것만 주석을 풀고 값을 채웁니다.

CI_ENVIRONMENT = development

app.baseURL = 'http://localhost:8080/'

database.default.hostname = 127.0.0.1
database.default.database = ci4board
database.default.username = ci4board
database.default.password = 비밀번호
database.default.DBDriver = MySQLi
database.default.DBPrefix =
database.default.port = 3306

database.default.charset  = utf8mb4
database.default.DBCollat = utf8mb4_unicode_ci

DB는 미리 만들어 둡니다.

CREATE DATABASE ci4board
  CHARACTER SET utf8mb4
  COLLATE utf8mb4_unicode_ci;

여기서 마지막 두 줄, charsetDBCollat 이 이번 편에서 하고 싶은 이야기입니다.


콜레이션은 왜 손으로 적어야 하는가

방금 DB를 utf8mb4_unicode_ci 로 만들었으니, 이 DB 안에 생기는 테이블도 당연히 그 콜레이션을 따를 거라고 생각하기 쉽습니다. 따르지 않습니다.

CodeIgniter의 Forge는 테이블을 만들 때 CREATE TABLE ... COLLATE=...명시적으로 붙입니다. 그리고 그 값은 DB 설정에서 가져오는데, .env 에 아무것도 안 적으면 프레임워크 기본값인 utf8mb4_general_ci 가 들어갑니다. DB를 어떻게 만들었든 상관없이요.

.env 에 콜레이션을 적지 않은 채로 마이그레이션을 돌리면 이렇게 됩니다.

+-------------+---------------------+
| TABLE_NAME  | TABLE_COLLATION     |
+-------------+---------------------+
| posts       | utf8mb4_general_ci  |
| boards      | utf8mb4_general_ci  |
| users       | utf8mb4_general_ci  |
+-------------+---------------------+

DB는 unicode_ci 인데 테이블은 전부 general_ci 입니다.

이게 왜 문제가 되는가

테이블이 전부 general_ci 로 통일되어 있다면 당장은 아무 일도 일어나지 않습니다. 문제는 나중에 다른 경로로 테이블이 하나 생길 때 입니다.

Forge를 거치지 않고 직접 SQL로 만든 테이블은 DB 기본값인 unicode_ci 를 따라갑니다. 그러면 그 테이블과 기존 테이블을 조인하는 순간 이렇게 됩니다.

SELECT * FROM t_gen g JOIN t_uni u ON g.slug = u.slug;
ERROR 1267 (HY000): Illegal mix of collations
(utf8mb4_general_ci,IMPLICIT) and (utf8mb4_unicode_ci,IMPLICIT) for operation '='

문자열 비교를 하려면 양쪽의 정렬 규칙이 같아야 하는데, 다르면 DB가 어느 쪽을 따라야 할지 알 수 없어서 그냥 거부합니다. slug 로 게시판을 찾는 것이 이 프로젝트의 가장 기본 동작이라는 걸 생각하면, 꽤 곤란한 자리에서 터지는 셈입니다.

이 강좌에서는 나중에 직접 SQL을 쓰는 마이그레이션이 실제로 하나 등장합니다. 그때 가서 원인을 찾느니, 지금 두 줄 적어두는 편이 낫습니다.

general_ciunicode_ci 중 무엇이 더 나은가는 이 자리에서 중요하지 않습니다. 섞이지 않게 하는 것이 중요합니다. 이 강좌는 utf8mb4_unicode_ci 로 통일합니다.


연결 확인

설정이 맞는지 확인합니다.

php spark db:table --show
+-----------+----------+----------+----------+----------+------+
| hostname  | database | username | DBDriver | DBPrefix | port |
+-----------+----------+----------+----------+----------+------+
| 127.0.0.1 | ci4board | ci4board | MySQLi   |          | 3306 |
+-----------+----------+----------+----------+----------+------+

Database has no tables!

접속 정보가 그대로 출력되고 "테이블이 없다"고 하면 성공입니다. 아직 아무것도 안 만들었으니 없는 게 맞습니다.

접속에 실패하면 이런 화면이 나옵니다.

[CodeIgniter\Database\Exceptions\DatabaseException]
Unable to connect to the database.
Main connection [MySQLi]: Connection refused

Connection refused 는 대개 DB 서버가 안 떠 있거나 포트가 다른 경우입니다. 계정이나 비밀번호가 틀리면 Access denied 가 나오니, 메시지를 보고 어느 쪽인지 구분하시면 됩니다.

DB는 무엇을 쓸까

MySQL 8.0 이상을 권합니다. MariaDB 10.6 이상도 됩니다. 다만 두 가지를 알아두시면 좋습니다.

  • MariaDB에서 JSON 은 진짜 타입이 아니라 LONGTEXT 에 "JSON이 맞는지 검사하는 제약"을 붙인 별칭입니다. 마이그레이션과 JSON 함수는 양쪽 다 똑같이 동작하니 진행에는 문제가 없습니다. 자세한 이야기는 18편에서 합니다.
  • 26편(검색)에서는 두 DB의 결과가 갈립니다. 그 편에서 양쪽을 나눠 다룹니다.

SQLite는 쓰지 않습니다. 지난 강좌에서는 운영 DB로 SQLite를 썼지만, 이번 강좌의 핵심인 JSON 생성 컬럼과 인덱스 전략이 MySQL/MariaDB 기준이기 때문입니다.


5. 폴더 컨벤션

기본 구조를 그대로 쓰되, 이 강좌에서 새로 쓸 자리 몇 곳만 미리 정해두겠습니다.

app/
├── Commands/          spark 커스텀 명령
├── Controllers/
│   ├── Board.php      게시판 (7편~)
│   └── Admin/         관리자 (29편~)
├── Database/
│   ├── Migrations/
│   └── Seeds/
├── Filters/           인증·권한 필터 (15편~)
├── Models/
├── Services/          BoardService 등 (8편~)
└── Views/
    ├── board/
    │   └── skins/     list / gallery / faq (28편)
    └── admin/

Services/ 라는 폴더가 눈에 띄실 겁니다. CodeIgniter가 정해준 자리는 아니고, 우리가 만드는 규칙입니다.

컨트롤러에 로직을 다 넣으면 게시판 설정을 읽어오는 코드가 컨트롤러마다 복사됩니다. 그런 코드가 갈 곳을 미리 만들어 둡니다. 나중에 BoardService 를 만들면서 왜 이 층이 필요한지 자연스럽게 보시게 될 겁니다.


코드를 남기는 규칙

이 강좌는 회차마다 코드를 태그로 고정해 원하는 시점부터 시작할 수 있도록 합니다.

먼저 .gitignore 를 확인합니다. appstarter가 기본으로 넣어주지만, 다음 항목이 들어 있는지 꼭 보세요.

/vendor/
.env
writable/cache/*
writable/logs/*
writable/session/*
writable/uploads/*

.env 에는 DB 비밀번호가 들어 있습니다. 이게 저장소에 올라가면 곤란합니다. 반대로 env (점 없는 예시 파일)는 올라가야 합니다. 다른 사람이 복사해서 쓸 원본이니까요.

커밋 메시지는 접두사를 붙입니다. 지난 강좌와 같은 규칙입니다.

feat:      기능 추가
fix:       버그 수정
refactor:  동작은 그대로, 구조만 개선
test:      테스트 코드
chore:     설정, 빌드, 잡일
docs:      문서

그리고 한 회차의 작업을 모두 마친 뒤 한 번에 커밋하고 태그를 붙입니다.

git add -A
git commit -m "chore: 프로젝트 세팅과 DB 연결"
git tag -a ep02 -m "ep02: 프로젝트 세팅과 이 강좌의 규칙"
git push origin main --tags

회차 중간에 커밋을 쪼개지 않는 이유는, 태그가 가리키는 지점이 "그 편을 다 읽고 난 뒤의 상태" 여야 하기 때문입니다. 독자가 git checkout ep02 했을 때 글의 마지막 문단과 코드가 일치해야 합니다.


확인하기

개발 서버를 띄웁니다.

php spark serve
CodeIgniter development server started on http://localhost:8080
Press Control-C to stop.

브라우저에서 http://localhost:8080 을 열어 CodeIgniter 기본 환영 화면이 보이면 됩니다.

이번 편을 마쳤을 때 상태는 이렇습니다.

  • php spark --version 이 4.7.x 를 출력한다
  • php spark db:table --show 가 접속 정보를 보여주고 "Database has no tables!" 라고 한다
  • .envDBCollat 이 적혀 있다
  • .gitignore.env 가 있고, git status.env 가 안 보인다
  • ep02 태그가 붙어 있다

이번 편 코드: GitHub 링크


마무리

빈 프로젝트 하나와 규칙 몇 줄이 전부입니다. 화면에는 아무 변화가 없습니다.

그래도 콜레이션 두 줄만큼은 챙겨가시면 좋겠습니다. 이런 종류의 설정은 잘못돼 있어도 한참 동안 아무 일도 일어나지 않다가, 프로젝트가 커진 뒤에 엉뚱한 자리에서 터집니다. 그때는 이미 테이블이 여덟 개라 되돌리기가 번거롭습니다.

다음 회차에서는 테이블 여덟 개의 설계도를 그립니다. 코드를 쓰기 전에 전체 구조를 먼저 확정하는데, 이것도 같은 이유입니다. 우리는 회차마다 태그를 찍기 때문에, 열 편쯤 가서 스키마를 바꾸면 이미 발행한 글과 태그를 전부 손봐야 합니다.

게시판

댓글 0

아직 댓글이 없습니다.

로그인 후 댓글을 남길 수 있습니다.

← 목록으로