# [실무 매뉴얼] 공용 SMS 본인인증 통합 구현 가이드

이 문서는 글로뷰(Globeau) 플랫폼에서 새로운 페이지나 폼에 **'휴대전화 SMS 본인인증 기능'**을 추가할 때, 처음부터 끝까지 빠짐없이 구현할 수 있도록 작성된 실무용 마스터 매뉴얼입니다.

이 가이드에 명시된 규칙(인터페이스, 변수명, 헬퍼 함수)을 준수하면, 기존에 구축된 스크립트(`sms_auth.js`)와 서비스(`Cafe24SmsService`)를 100% 재활용하여 단시간 내에 인증 기능을 복제할 수 있습니다.

---

## 1. 사전 환경 설정 (.env)

외부 카페24 API 통신 및 테스트 모드 전환을 위해 프로젝트 환경 변수 파일에 다음 값이 반드시 설정되어야 합니다.

    CAFE24_SMS_ID=your_cafe24_id
    CAFE24_SMS_KEY=your_cafe24_api_secure_key
    CAFE24_SMS_SENDER=0212345678
    SMS_TEST_MODE=false

* **참고**: `SMS_TEST_MODE=true` 설정 시, 실제 문자를 발송하지 않고 성공 응답과 함께 가상 인증번호를 반환하여 개발 비용을 절감합니다.

---

## 2. 폼 렌더링 컨트롤러 데이터 준비 (예: ShopController)

뷰(Blade)를 렌더링하기 전, 컨트롤러에서는 반드시 **1) 기존 인증 상태**와 **2) 유효성 검사 실패 복귀(`old()`) 상태**를 모두 체크하여 뷰로 전달해야 합니다. 전화번호는 전역 헬퍼 함수 `format_phone()`을 사용하여 하이픈(`-`)이 포함된 형태로 정제합니다.

    public function create()
    {
        $user = Auth::user();
        
        // [핵심 1] 유효성 검사 실패(old) 또는 기존 DB 인증 상태 복원
        $isVerifiedOld = old('is_phone_verified') === 'true';$hasVerifiedPhone = $isVerifiedOld \vert{}\vert{} ($user && $user->is_phone_verified &&$user->phone);

        // [핵심 2] 입력값 복원 및 헬퍼 함수(format_phone)를 통한 포맷팅 적용
        $rawPhone = old('phone', ($user ? $user->phone : ''));$formattedPhone = $rawPhone ? format_phone($rawPhone) : '';

        return view('shops.create', compact('hasVerifiedPhone', 'formattedPhone'));
    }

---

## 3. 프론트엔드 UI 규격 (Blade 템플릿 마크업)

`sms_auth.js` 스크립트가 DOM 요소를 제어할 수 있도록, 폼 내부에 반드시 **`data-sms-role` 속성**을 규격에 맞게 삽입해야 합니다. 아래 마크업 골격을 복사하여 필요한 곳에 스타일만 덧입혀 사용하십시오.

    <!-- [필수 1] 인증 완료 상태 플래그 (유효성 검사 방어용 hidden 필드) -->
    <input type="hidden" id="is_phone_verified" name="is_phone_verified" 
           data-sms-role="verified-flag" 
           value="{{ old('is_phone_verified', ($hasVerifiedPhone ? 'true' : 'false')) }}">

    <!-- [필수 2] 전화번호 입력 및 버튼 그룹 -->
    <div data-sms-role="send-area">
        <div class="input-group">
            <!-- 전화번호 입력창 -->
            <input type="text" class="form-control @if($hasVerifiedPhone) bg-light @endif" 
                   id="phone" name="phone" 
                   value="{{ $formattedPhone }}" 
                   data-sms-role="phone" 
                   maxlength="13" 
                   @if($hasVerifiedPhone) readonly @endif>

            <!-- 번호 수정 버튼 (인증 완료 시 노출) -->
            <button type="button" class="btn btn-premium @if(!$hasVerifiedPhone) d-none @endif" 
                    data-sms-role="edit-btn">
                번호 수정
            </button>

            <!-- 인증번호 전송 버튼 (미인증 시 노출) -->
            <button type="button" class="btn btn-premium @if($hasVerifiedPhone) d-none @endif" 
                    data-sms-role="send-btn">
                <span class="btn-text">인증번호 전송</span>
            </button>
        </div>
        <!-- 전송 결과 메시지 출력 박스 -->
        <div data-sms-role="message-box" class="d-none"></div>
    </div>

    <!-- [필수 3] 인증번호 6자리 입력 및 확인 영역 (초기 숨김 처리) -->
    <div class="d-none" data-sms-role="verify-area">
        <span id="auth-timer">3:00</span>
        
        <input type="text" id="sms_code" name="sms_code" data-sms-role="code-input" maxlength="6">
        
        <button type="button" data-sms-role="verify-btn">인증 확인</button>
        <button type="button" data-sms-role="resend-btn">번호 재입력</button>
    </div>

---

## 4. 백엔드 인증 핵심 로직 (VerifyPhoneController)

본 뼈대 위에서 AJAX 요청은 `VerifyPhoneController`로 향합니다. 해당 컨트롤러는 다음의 파이프라인을 거쳐 안전하게 인증을 완료합니다. (이미 구현되어 있으므로 엔드포인트 URL만 매칭 확인)

1. **검증 및 방어**: 요청 IP 기반 Rate Limiting (1분 10회), 번호/세션별 일일 제한(5회/20회), 3분 쿨타임을 체크합니다.
2. **코드 발송**: `Cafe24SmsService->sendVerificationCode()`를 호출하여 실제 문자를 발송하고, `sms_logs` 테이블에 DB 이력을 적재합니다.
3. **인증 확인 (verifySms)**: 사용자가 입력한 6자리 코드와 Cache 데이터를 대조하고, 일치할 경우 중복 계정 여부를 판단한 뒤 로그인 유저(`users` 테이블)의 `is_phone_verified` 및 `phone` 컬럼을 갱신합니다.

---

## 5. 구현 체크리스트

새로운 페이지에 기능 적용을 완료한 뒤 아래 항목을 테스트하십시오.
* [ ] DB에 등록된 번호가 하이픈(`-`)이 포함된 형태로 입력창에 잘 출력되는가?
* [ ] 이미 인증된 상태일 경우 입력창이 `readonly`로 잠겨있고 `[번호 수정]` 버튼이 보이는가?
* [ ] 폼의 다른 필드에서 유효성 검사 에러가 발생해 화면이 새로고침되어도, 전화번호 인증 상태가 그대로 유지되는가?