학과 공용 스터디룸(622호) 예약 및 관리 시스템
이메일/비밀번호로 로그인하여 회의실을 예약하고 관리할 수 있는 웹 애플리케이션입니다.
- 이메일/비밀번호 회원가입 & 로그인
@gachon.ac.kr이메일만 가입/로그인 허용- 이메일 인증 코드 및 비밀번호 재설정 지원
- 예약 생성
- 날짜/시간 선택
- 좌석 선택 및 여러 시간 구간 예약
- 다중 사용자 참여 (공동 예약)
- 내 예약 조회
- 내가 생성한 예약 목록 확인
- 예약 상세 정보 보기
- 예약 취소
- 불필요한 예약 삭제
- 622호 스터디룸 예약 지원
- 회의실별 예약 내역 추적
- 향후 다른 회의실 추가 가능한 구조
- 사용자 프로필 정보 확인
- 공지, 페널티 및 예약 관련 알림 제공
- 회원, 허용 이메일, 관리자 및 페널티 관리
- 예약 현황·취소 내역 및 열쇠함 URL 관리
- 공지사항과 서비스 개선 요청 관리
- 7일/30일 기준 회원·예약 사용 통계 제공
- Microsoft Clarity를 통한 운영 환경의 세션 기록과 히트맵 수집
- 로컬 환경에서는 추적하지 않으며, 민감 정보는 Clarity 마스킹 설정으로 보호
| 기술 | 버전 | 용도 |
|---|---|---|
| PHP | 7.4+ | 서버사이드 언어 (composer.json 기준) |
| Laravel | 8.x | PHP 프레임워크 |
| MySQL | 8.0+ | 관계형 데이터베이스 |
| 기술 | 용도 |
|---|---|
| Blade | Laravel 템플릿 엔진 |
| Webpack (Laravel Mix) | 프론트엔드 빌드 도구 |
| Tailwind CSS | 스타일링 |
| Alpine.js / JavaScript | 화면 상태 및 클라이언트 스크립트 |
| 도구 | 용도 |
|---|---|
| Composer | PHP 의존성 관리 |
| npm | JS 의존성 관리 |
| Git | 버전 관리 |
Remit/
├── app/
│ ├── Http/
│ │ └── Controllers/
│ │ ├── AuthController.php # 회원가입/로그인(세션)
│ │ ├── ReservationController.php # 예약 CRUD
│ │ ├── MypageController.php # 마이페이지
│ │ ├── NotificationController.php # 알림
│ │ └── AdminController.php # 관리자 및 통계
│ ├── Models/
│ │ ├── User.php # 사용자 모델
│ │ ├── Room.php # 회의실 모델
│ │ ├── Reservation.php # 예약 모델
│ │ ├── ReservationUser.php # 예약-사용자 연결
│ │ └── UsageLog.php # 사용 기록
│ └── Providers/
│ └── AppServiceProvider.php
├── config/
│ ├── auth.php # 인증 설정
│ ├── database.php # DB 설정
│ ├── services.php # 외부 서비스 설정
│ └── ... # 기타 Laravel 설정
├── database/
│ ├── migrations/ # 데이터베이스 스키마
│ │ ├── *_create_users_table.php
│ │ ├── *_add_google_fields_to_users_table.php
│ │ ├── *_create_rooms_table.php
│ │ ├── *_create_reservations_table.php
│ │ ├── *_create_reservation_users_table.php
│ │ ├── *_create_usage_logs_table.php
│ │ ├── *_create_cache_table.php
│ │ └── *_create_jobs_table.php
│ ├── seeders/
│ │ ├── DatabaseSeeder.php
│ │ └── RoomSeeder.php # 622호 초기 데이터
│ └── factories/
│ └── UserFactory.php
├── public/
│ ├── index.php # 애플리케이션 진입점
│ └── ...
├── resources/
│ ├── views/
│ │ ├── layouts/
│ │ │ └── app.blade.php # 기본 레이아웃
│ │ ├── admin/
│ │ │ └── statistics.blade.php # 관리자 사용 통계
│ │ ├── auth/
│ │ │ └── login.blade.php # 로그인 페이지
│ │ ├── reservation/
│ │ │ ├── index.blade.php # 예약하기
│ │ │ └── my.blade.php # 내 예약
│ │ ├── mypage/
│ │ │ └── index.blade.php # 마이페이지
│ │ └── notification/
│ │ └── index.blade.php # 알림
│ ├── css/
│ │ └── app.css
│ └── js/
│ ├── app.js
│ └── bootstrap.js
├── routes/
│ ├── web.php # 웹 라우트 정의
│ └── console.php
├── storage/ # 로그, 캐시, 세션 등
├── tests/ # 테스트 코드
├── .env # 환경 변수 (Git 제외)
├── .gitignore
├── composer.json # PHP 의존성
├── package.json # JS 의존성
├── webpack.mix.js # Webpack(Mix) 설정
└── README.md
- PHP 7.4 이상
- Composer
- MySQL 8.0 이상
- Node.js 18+ (npm 권장)
git clone <repository-url>
cd Remit# PHP 의존성
composer install
# JavaScript 의존성
npm installcp .env.example .env
php artisan key:generatemysql -u root -pMySQL 쉘에서
CREATE DATABASE remit CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
EXIT;DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=remit
DB_USERNAME=root
DB_PASSWORD=your_passwordphp artisan migrate --seed이 명령어는:
- 모든 테이블 생성 (users, rooms, reservations 등)
- 622호 스터디룸 초기 데이터 삽입
브라우저에서 아래로 접속해 계정을 생성/로그인합니다.
- 회원가입:
/register - 로그인:
/login
참고: 과거 Google OAuth로 생성된 계정이 DB에 남아있더라도, 해당 이메일로
/register에서 비밀번호를 설정하면(비밀번호가 비어있는 경우) 동일 계정으로 로그인할 수 있습니다.
npm run build개발 중에는:
npm run devphp artisan serve브라우저에서 http://localhost:8000 접속
./start.sh이 스크립트는:
- 서버를
0.0.0.0:8000에 바인딩 (모든 네트워크 인터페이스) - 로컬 IP 주소를 자동으로 감지하여 표시
- 모바일 접속 안내 메시지 출력
출력 예시:
📍 Server will be accessible at:
- Local: http://localhost:8000
- Network: http://172.30.1.85:8000
📱 To access from mobile device:
1. Connect your phone to the same Wi-Fi network
2. Open browser and go to: http://172.30.1.85:8000
서버를 실행하면 start.sh가 자동으로 표시합니다.
수동으로 확인하려면:
# macOS
ipconfig getifaddr en0
# 또는
ifconfig | grep "inet " | grep -v 127.0.0.1APP_URL=http://172.30.1.85:8000 # 실제 로컬 IP로 변경- 같은 Wi-Fi에 연결된 모바일 기기에서
- 브라우저로
http://172.30.1.85:8000접속
⚠️ 주의: IP 주소는 네트워크에 따라 바뀔 수 있습니다. Wi-Fi가 변경되면 위 단계를 다시 수행하세요.
cp .env.example .env
php artisan key:generateAPP_NAME=Remit
APP_ENV=local
APP_KEY= # php artisan key:generate로 자동 생성
APP_DEBUG=true
APP_TIMEZONE=Asia/Seoul
APP_URL=http://localhost:8000
APP_LOCALE=ko
APP_FALLBACK_LOCALE=enDB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=remit
DB_USERNAME=root
DB_PASSWORD= # MySQL 비밀번호 (없으면 비워두기)SESSION_DRIVER=database
SESSION_LIFETIME=120
CACHE_STORE=database
QUEUE_CONNECTION=database# 특정 키워드가 포함된 사용자만 허용 (예: "철학과")
ALLOWED_USER_FILTER=
# 관리자 계정과 문의 메일 수신자
ADMIN_EMAIL=admin@example.com
ADMIN_CONTACT_EMAIL=admin@example.com
# 로그인/회원가입 화면에 노출되는 "사용법(영상)"(유튜브) 링크
HELP_VIDEO_URL=
# 내부 IP만 접근시키려는 경우
INTERNAL_IP_FILTER_ENABLED=false
INTERNAL_IP_ALLOWED_CIDRS=127.0.0.1,::1MAIL_MAILER=smtp
MAIL_HOST=smtp.gmail.com
MAIL_PORT=587
MAIL_USERNAME=
MAIL_PASSWORD=
MAIL_ENCRYPTION=tls
MAIL_FROM_ADDRESS=no-reply@example.com
MAIL_FROM_NAME="${APP_NAME}"관리자 계정은 /admin/statistics에서 실제 DB의 회원 및 예약 현황을 확인할 수 있습니다.
- 조회 기간: 최근 7일 또는 30일
- 회원: 전체 일반 회원, 기간 신규 회원, 기간 예약 이용자
- 예약: 전체·유효·취소 예약, 취소율 및 취소 주체
- 이용: 재이용자, 예약당 평균 참여자, 인기 시간대와 공간
- 추이: 7일은 일별, 30일은 주별 집계
예약 지표는 선택 기간 안에 **시작하는 예약의 start_at**을 기준으로 계산합니다. 외부 분석 도구의 방문자 수와 실제 예약 수는 정의가 다르므로 운영 지표는 이 페이지를 기준으로 확인합니다.
공통 레이아웃 resources/views/layouts/app.blade.php에 Microsoft Clarity가 설치되어 있습니다.
- Clarity Project ID:
y58auzrycx @production조건으로 운영 환경에서만 실행- 결과 확인: Microsoft Clarity의 Dashboard, Recordings, Heatmaps
- 설치 이후의 방문부터 수집되며 과거 데이터는 소급되지 않음
- 회원 정보와 예약 값 보호를 위해 Clarity의 Masking 설정 유지 권장
Clarity에는 이메일, 학번 등 직접 식별자를 사용자 ID나 커스텀 값으로 전송하지 않습니다. 실제 회원·예약 수치는 자체 관리자 통계에서 확인하고, Clarity는 클릭·스크롤·이탈 흐름 분석에 사용합니다.
현재 운영 환경은 가비아 PHP 웹호스팅이며, FileZilla에서 보이는 원격 /가 애플리케이션 경로 /www_root에 해당합니다.
composer install --no-dev --optimize-autoloader
npm install
npm run build서버에서 Composer 또는 npm을 실행할 수 없다면 로컬에서 준비한 vendor와 public/css, public/js, public/mix-manifest.json을 함께 배포합니다. node_modules, tests, Docker 관련 파일은 운영 실행에 필요하지 않습니다.
- 서버 루트의
composer.json은 Blade 컴포넌트의 애플리케이션 네임스페이스 판별에 필요하므로 반드시 유지합니다. composer.lock도 함께 배포해 운영 의존성 버전을 고정합니다.- 로컬
.env를 운영 서버에 업로드하지 않습니다. 운영.env는 서버에서 별도로 관리하고 Git에 커밋하지 않습니다. database/migrations파일을 올리는 것만으로 DB가 변경되지는 않습니다.- 마이그레이션 전 DB를 백업한 다음
php artisan migrate --force를 실행합니다. storage와bootstrap/cache는 PHP 프로세스가 쓸 수 있어야 합니다.
APP_ENV=production
APP_DEBUG=false
APP_URL=https://your-domain.example
APP_FORCE_HTTPS=true
DB_CONNECTION=mysql
DB_HOST=your-db-host
DB_PORT=3306
DB_DATABASE=your-db-name
DB_USERNAME=your-db-user
DB_PASSWORD=your-db-password
SESSION_DRIVER=database
SESSION_SECURE_COOKIE=trueHTTPS 인증서가 정상 적용되기 전에는 APP_FORCE_HTTPS=false, SESSION_SECURE_COOKIE=false로 운영하고, 인증서 적용 직후 HTTPS 설정으로 전환합니다.
배포 후 서버에서 실행할 수 있다면 다음 캐시 정리를 수행합니다.
php artisan config:clear
php artisan view:clear
php artisan cache:clearSSH를 사용할 수 없다면 FileZilla에서 storage/framework/views 안의 생성된 .php 파일만 제거해 뷰 캐시를 갱신할 수 있습니다. 디렉터리 자체와 .gitignore는 유지합니다.
| 컬럼 | 타입 | 설명 |
|---|---|---|
| id | bigint | PK |
| name | string | 사용자 이름 |
| string | 이메일 (unique) | |
| google_id | string | 과거 Google OAuth 호환 ID (nullable, unique) |
| password | string | 해시된 비밀번호 (nullable) |
| role | string | user 또는 admin |
| warning | integer | 경고 횟수 |
| student_id | string | 학번 (nullable) |
| terms_agreed_at | timestamp | 약관 동의 시각 (nullable) |
| suspended_at | timestamp | 이용 정지 시각 (nullable) |
| remember_token | string | 로그인 유지 토큰 |
| created_at | timestamp | 생성 시간 |
| updated_at | timestamp | 수정 시간 |
| 컬럼 | 타입 | 설명 |
|---|---|---|
| id | bigint | PK |
| name | string | 회의실 이름 (예: "622호") |
| description | text | 회의실 설명 |
| created_at | timestamp | 생성 시간 |
| updated_at | timestamp | 수정 시간 |
| 컬럼 | 타입 | 설명 |
|---|---|---|
| id | bigint | PK |
| room_id | bigint | FK (예약한 회의실) |
| seat_id | bigint | FK (선택 좌석, nullable) |
| seat_no | integer | 레거시 좌석 번호 (nullable) |
| group_member_emails | json | 공동 예약자 이메일 (nullable) |
| start_at | datetime | 예약 시작 시간 |
| end_at | datetime | 예약 종료 시간 |
| key_code | string | 열쇠함 코드 |
| status | string | confirmed 또는 cancelled |
| cancelled_at | datetime | 취소 시각 (nullable) |
| cancelled_by | string | user 또는 admin (nullable) |
| cancel_reason | text | 취소 사유 (nullable) |
| created_at | timestamp | 생성 시간 |
| updated_at | timestamp | 수정 시간 |
관계:
room_id→rooms.idseat_id→seats.id- 예약 참여자는
reservation_users피벗 테이블을 통해users와 연결
| 컬럼 | 타입 | 설명 |
|---|---|---|
| id | bigint | PK |
| reservation_id | bigint | FK (예약) |
| user_id | bigint | FK (참여 사용자) |
| is_representative | boolean | 대표 예약자 여부 |
| created_at | timestamp | 생성 시간 |
| updated_at | timestamp | 수정 시간 |
관계:
reservation_id→reservations.iduser_id→users.id
용도: 한 예약에 여러 사용자가 참여할 수 있도록 연결
| 컬럼 | 타입 | 설명 |
|---|---|---|
| id | bigint | PK |
| user_id | bigint | FK (사용자) |
| reservation_id | bigint | FK (예약, nullable) |
| entry_at | datetime | 입실 시간 |
| exit_at | datetime | 퇴실 시간 (nullable) |
| duration | integer | 사용 시간(초, nullable) |
| created_at | timestamp | 생성 시간 |
| updated_at | timestamp | 수정 시간 |
관계:
user_id→users.idreservation_id→reservations.id
용도: 회의실 입/퇴실 이력 추적
seats- 공간별 좌석notifications,notices- 사용자 알림과 공지allowed_emails,email_verification_codes- 가입 허용 및 이메일 인증lockbox_urls- 열쇠함 URLerror_reports- 서비스 개선 요청sessions,password_reset_tokens- 인증 및 세션cache,cache_locks- 데이터베이스 기반 캐시jobs,job_batches,failed_jobs- 큐 작업 관리
| Method | URI | 설명 |
|---|---|---|
| GET | / |
루트 (로그인으로 리다이렉트) |
| GET | /login |
로그인 페이지 |
| GET | /register |
회원가입 페이지 |
| POST | /register/send-code |
이메일 인증 코드 발송 |
| POST | /register/verify-code |
이메일 인증 코드 확인 |
| GET | /forgot-password |
비밀번호 재설정 요청 |
| Method | URI | 설명 |
|---|---|---|
| GET | /reservation |
예약하기 페이지 |
| POST | /reservation |
예약 생성 |
| POST | /reservation/available-seats |
예약 가능 좌석 조회 |
| DELETE | /reservation/{id} |
예약 삭제 |
| GET | /reservation/my |
내 예약 조회 |
| GET | /mypage |
마이페이지 |
| GET | /notifications |
알림 |
| POST | /logout |
로그아웃 |
| Method | URI | 설명 |
|---|---|---|
| GET | /admin |
관리자 대시보드 |
| GET | /admin/statistics |
회원·예약 사용 통계 |
| GET | /admin/users |
회원 및 허용 이메일 관리 |
| GET | /admin/reservations |
현재 예약 관리 |
| GET | /admin/reservations/history |
예약·취소 이력 |
| GET | /admin/notices |
공지 관리 |
| GET | /admin/error-reports |
서비스 개선 요청 관리 |
관리자 라우트는 인증 후 접근하며, 각 관리자 액션에서 관리자 권한을 추가로 확인합니다.
# MySQL 서비스 확인
mysql --version
# MySQL 시작
brew services start mysql # macOS
sudo systemctl start mysql # Linux
# root 비밀번호 재설정 (필요 시)
sudo mysql -u root
ALTER USER 'root'@'localhost' IDENTIFIED WITH mysql_native_password BY 'new_password';
FLUSH PRIVILEGES;php artisan cache:clear
php artisan config:clear
php artisan view:clear
php artisan route:clear다음 오류가 로그에 나타나면 서버 애플리케이션 루트에 composer.json을 다시 배포합니다.
file_get_contents(/www_root/composer.json): failed to open stream: No such file or directory
composer.json은 PHP 패키지 설치용일 뿐 아니라 Laravel이 Blade 클래스 컴포넌트의 App\\ 네임스페이스를 판별할 때도 사용합니다.
브라우저의 500 Internal Server Error는 결과만 보여줍니다. 실제 원인은 서버의 storage/logs/laravel.log에서 가장 최근 production.ERROR 항목을 확인합니다. 운영 환경에서는 오류 상세가 사용자 화면에 노출되지 않도록 APP_DEBUG=false를 유지합니다.