瀏覽代碼

인플루언서 회원가입 완료

DESKTOP-T61HUSC\user 4 月之前
父節點
當前提交
520d29dd09

+ 1 - 1
assets/scss/mode-w-m.scss

@@ -6763,7 +6763,7 @@ z
       .login-input-wrap{
         .txt-field-box{
           &:first-of-type{
-            margin-bottom: 0.87rem;
+            margin-bottom: 0.5rem;
           }
           .v-input{
             &.custom-input{

+ 15 - 7
assets/scss/style.scss

@@ -1037,7 +1037,7 @@ p.success-txt {
 
         .v-label {
           height: auto;
-          padding-left: 0.94rem;
+          padding-left: 0.4rem;
           margin: 0;
           font-size: 0.81rem;
           font-weight: 400;
@@ -3554,12 +3554,16 @@ p.success-txt {
       background-color: #fff;
       width: 30%;
       max-width: 1000px;
-      padding: 3rem!important;
+      padding: 2.5rem!important;
       .login-r{
         height: 100%;
         overflow-x: hidden;
         overflow-y: auto;
         padding: 0;
+        .login-input-wrap{
+          max-height: calc(100vh - 400px);
+          overflow-y: auto;
+        }
         .login-btn-wrap{
           margin-bottom: 0;
         }
@@ -3765,12 +3769,12 @@ p.success-txt {
       }
       
       .tit-login {
-        margin-bottom: 2rem;
+        margin-bottom: 1.5rem;
         text-align: center;
 
         strong {
           color: #2d3748;
-          font-size: 2rem;
+          font-size: 1.8rem;
           font-weight: 700;
           background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
           -webkit-background-clip: text;
@@ -3780,12 +3784,16 @@ p.success-txt {
       }
 
       .login-input-wrap {
-        margin-bottom: 1.5rem;
+        margin-bottom: 1rem;
 
         .txt-field-box {
-          margin-bottom: 1rem;
+          margin-bottom: 0.5rem;
           position: relative;
           display: flex;
+
+          &:last-child{
+            margin-bottom: 0;
+          }
           
           .custom-input {
             .v-input__control {
@@ -6345,7 +6353,7 @@ body:has(.darkmode) {
     content:'간편 로그인';
     display: inline-block;
     padding:5px 10px;
-    background: #fff;
+    background: #F8F7FC;
     position: absolute;
     top: -13px;
     left: 50%;

+ 2 - 1
backend/app/Config/Routes.php

@@ -69,7 +69,8 @@ $routes->post('deli/itemlist', 'Deli::itemlist');
 $routes->post('deli/list', 'Deli::delilist');
 
 // 마이페이지 라우트
-$routes->post('mypage/detail', 'Mypage::myDetail/$1');
+$routes->post('mypage/detail', 'Mypage::myDetail');
+$routes->post('mypage/update', 'Mypage::myUpdate');
 
 // 고객센터 라우트
 $routes->post('cs/list', 'Cs::csList');

文件差異過大導致無法顯示
+ 817 - 800
backend/app/Controllers/Auth.php


+ 101 - 0
backend/app/Controllers/Mypage.php

@@ -85,4 +85,105 @@ class Mypage extends ResourceController
             ], 500);
         }
     }
+
+    // 사용자 정보 수정
+    public function myUpdate(){
+        // 한국 시간으로 설정
+        date_default_timezone_set('Asia/Seoul');
+
+        $db = \Config\Database::connect();
+        $request = $this->request->getJSON(true);
+        $memberType = isset($request['MEMBER_TYPE']) ? $request['MEMBER_TYPE'] : null;
+        $memberSeq = isset($request['MEMBER_SEQ']) ? $request['MEMBER_SEQ'] : null;
+
+        try {
+            // 멤버 타입에 따라 다른 테이블에서 업데이트
+            switch ($memberType) {
+                case 'INFLUENCER':
+                case 'I':
+                    $table = 'USER_LIST';
+                    break;
+
+                case 'VENDOR':
+                case 'V':
+                    $table = 'VENDOR_LIST';
+                    break;
+
+                case 'BRAND':
+                case 'B':
+                    $table = 'BRAND_LIST';
+                    break;
+
+                default:
+                    return $this->respond([
+                        'status' => 'fail',
+                        'message' => '알 수 없는 회원 타입입니다.'
+                    ], 400);
+            }
+
+            // 업데이트할 데이터
+            $data = [];
+            
+            // 공통 필드
+            if (isset($request['NAME'])) {
+                $data['NAME'] = $request['NAME'];
+            }
+            if (isset($request['EMAIL'])) {
+                $data['EMAIL'] = $request['EMAIL'];
+            }
+            if (isset($request['PHONE'])) {
+                $data['PHONE'] = $request['PHONE'];
+            }
+            if (isset($request['SNS_TYPE'])) {
+                $data['SNS_TYPE'] = $request['SNS_TYPE'];
+            }
+            if (isset($request['SNS_LINK_ID'])) {
+                $data['SNS_LINK_ID'] = $request['SNS_LINK_ID'];
+            }
+            if (isset($request['PASSWORD']) && !empty($request['PASSWORD'])) {
+                $data['PASSWORD'] = password_hash($request['PASSWORD'], PASSWORD_DEFAULT);
+            }
+            
+            // 인플루언서 전용 필드
+            if (($memberType === 'INFLUENCER' || $memberType === 'I') && isset($request['NICK_NAME'])) {
+                $data['NICK_NAME'] = $request['NICK_NAME'];
+            }
+            
+            // 벤더/브랜드 전용 필드
+            if (($memberType !== 'INFLUENCER' && $memberType !== 'I') && isset($request['COMPANY_NAME'])) {
+                $data['COMPANY_NAME'] = $request['COMPANY_NAME'];
+            }
+
+            if (empty($data)) {
+                return $this->respond([
+                    'status' => 'fail',
+                    'message' => '업데이트할 데이터가 없습니다.'
+                ], 400);
+            }
+
+            // 업데이트 실행
+            $result = $db->table($table)
+                ->where('SEQ', $memberSeq)
+                ->update($data);
+
+            if ($result) {
+                return $this->respond([
+                    'status' => 'success',
+                    'message' => '사용자 정보가 성공적으로 업데이트되었습니다.',
+                    'data' => $data
+                ], 200);
+            } else {
+                return $this->respond([
+                    'status' => 'fail',
+                    'message' => '업데이트에 실패했습니다.'
+                ], 500);
+            }
+
+        } catch (\Exception $e) {
+            return $this->respond([
+                'status' => 'fail',
+                'message' => 'DB 오류: ' . $e->getMessage()
+            ], 500);
+        }
+    }
 }

+ 445 - 99
pages/auth/join.vue

@@ -28,7 +28,7 @@
               class="custom-input"
               @blur="checkId"
             ></v-text-field>
-            <v-btn v-if="!useStore.getSnsTempData?.ID" small class="ml-2" @click="checkId"
+            <v-btn v-if="!useStore.getSnsTempData?.ID" class="ml-2 custom-btn mini btn-purple" @click="checkId"
               >중복확인</v-btn
             >
           </div>
@@ -37,7 +37,7 @@
             <v-text-field
               v-model="form.formValue2"
               :type="visible ? 'text' : 'password'"
-              placeholder="패스워드를 입력해주세요."
+              placeholder="비밀번호를 입력해주세요."
               class="custom-input"
               id="password"
             ></v-text-field>
@@ -53,7 +53,7 @@
             <v-text-field
               v-model="form.formValue3"
               :type="visible ? 'text' : 'password'"
-              placeholder="패스워드 확인"
+              placeholder="비밀번호를 한 번 더 입력해주세요."
               class="custom-input"
             ></v-text-field>
             <i
@@ -129,25 +129,33 @@
               class="custom-input"
               placeholder=""
             ></v-text-field>
-            <span v-if="form.formValue11 != 'direct'">@</span>
-            <v-select
+            <span>@</span>
+            <v-combobox
               :disabled="useStore.getSnsTempData?.EMAIL ? true : false"
               v-model="form.formValue11"
-              :items="form.formValueItems11"
-              item-title="text"
-              item-value="value"
+              :items="['gmail.com', 'naver.com', 'daum.net', 'hotmail.com', 'yahoo.com']"
+              placeholder="도메인 선택 또는 직접 입력"
               class="custom-select"
-            ></v-select>
+            ></v-combobox>
           </div>
 
-          <div class="txt-field-box">
+          <div class="d-flex agree--box">
+            <v-checkbox hide-details class="custom-check type2" v-model="formVendor.formValue13" @click="handlePrivacyModal">
+              <template v-slot:label>개인정보약관동의(필수)</template>
+            </v-checkbox>
+            <v-checkbox hide-details class="custom-check type2" v-model="formVendor.formValue14" @click="handleThirdPartyModal">
+              <template v-slot:label>제3자 정보동의(선택)</template>
+            </v-checkbox>
+          </div>
+
+          <!-- <div class="txt-field-box">
             <v-textarea
               v-model="form.formValue13"
               placeholder="자기소개를 입력해주세요. 벤더사들이 참고할 수 있도록 작성해주세요."
               class="custom-textarea"
               rows="3"
             ></v-textarea>
-          </div>
+          </div> -->
         </div>
 
         <div v-show="form.formValue0 === 'N'" class="login-input-wrap">
@@ -242,21 +250,20 @@
               class="custom-input"
               placeholder=""
             ></v-text-field>
-            <span v-if="formVendor.formValue11 != 'direct'">@</span>
-            <v-select
+            <span>@</span>
+            <v-combobox
               v-model="formVendor.formValue11"
-              :items="formVendor.formValueItems11"
-              item-title="text"
-              item-value="value"
+              :items="['gmail.com', 'naver.com', 'daum.net', 'hotmail.com', 'yahoo.com']"
+              placeholder="도메인 선택 또는 직접 입력"
               class="custom-select"
-            ></v-select>
+            ></v-combobox>
           </div>
 
           <div class="mt-5 d-flex agree--box">
-            <v-checkbox class="custom-check type2" v-model="formVendor.formValue13">
+            <v-checkbox class="custom-check type2" v-model="formVendor.formValue13" @click="handlePrivacyModal">
               <template v-slot:label>개인정보약관동의</template>
             </v-checkbox>
-            <v-checkbox class="custom-check type2" v-model="formVendor.formValue14">
+            <v-checkbox class="custom-check type2" v-model="formVendor.formValue14" @click="handleThirdPartyModal">
               <template v-slot:label>제3자 정보동의</template>
             </v-checkbox>
           </div>
@@ -292,6 +299,80 @@
         <p>마포구 합정동</p>
       </div>
     </div>
+
+    <!-- 개인정보 약관 모달 -->
+    <div v-if="showPrivacyModal" class="modal-overlay" @click="closePrivacyModal">
+      <div class="modal-content" @click.stop>
+        <div class="modal-header">
+          <h3>개인정보 수집 및 이용 동의</h3>
+          <button class="modal-close-btn" @click="closePrivacyModal">×</button>
+        </div>
+        <div class="modal-body">
+          <div class="privacy-content">
+            <h4>1. 개인정보 수집 목적</h4>
+            <p>서비스 제공, 회원 관리, 고객 상담, 마케팅 활용</p>
+            
+            <h4>2. 수집하는 개인정보 항목</h4>
+            <p>필수항목: 이름, 이메일, 전화번호, 회원구분</p>
+            <p>선택항목: 닉네임, SNS 정보</p>
+            
+            <h4>3. 개인정보 보유 및 이용기간</h4>
+            <p>회원 탈퇴 시까지 또는 법정 보존기간</p>
+            
+            <h4>4. 개인정보 제3자 제공</h4>
+            <p>원칙적으로 개인정보를 제3자에게 제공하지 않습니다. 단, 법령에 의한 경우는 예외로 합니다.</p>
+            
+            <h4>5. 개인정보 처리 위탁</h4>
+            <p>서비스 운영을 위해 필요한 경우 개인정보 처리를 위탁할 수 있습니다.</p>
+            
+            <h4>6. 개인정보 보호책임자</h4>
+            <p>개인정보 보호책임자: 홍길동 (privacy@company.com)</p>
+            
+            <p class="privacy-notice">
+              위의 개인정보 수집 및 이용에 대한 동의를 거부할 권리가 있습니다. 
+              그러나 동의를 거부할 경우 서비스 이용이 제한될 수 있습니다.
+            </p>
+          </div>
+        </div>
+        <div class="modal-footer">
+          <button class="btn-disagree" @click="disagreePrivacy">미동의</button>
+          <button class="btn-agree" @click="agreePrivacy">동의</button>
+        </div>
+      </div>
+    </div>
+
+    <!-- 제3자 정보 제공 동의 모달 -->
+    <div v-if="showThirdPartyModal" class="modal-overlay" @click="closeThirdPartyModal">
+      <div class="modal-content" @click.stop>
+        <div class="modal-header">
+          <h3>제3자 정보 제공 동의</h3>
+          <button class="modal-close-btn" @click="closeThirdPartyModal">×</button>
+        </div>
+        <div class="modal-body">
+          <div class="privacy-content">
+            <h4>1. 제공받는 자</h4>
+            <p>협력업체, 마케팅 대행사</p>
+            
+            <h4>2. 제공하는 개인정보 항목</h4>
+            <p>이름, 이메일, 전화번호, 관심사</p>
+            
+            <h4>3. 제공 목적</h4>
+            <p>맞춤형 광고, 이벤트 정보 제공, 협력업체 서비스 안내</p>
+            
+            <h4>4. 보유 및 이용기간</h4>
+            <p>제공 목적 달성 시까지 또는 동의 철회 시까지</p>
+            
+            <p class="privacy-notice">
+              제3자 정보 제공에 대한 동의는 선택사항이며, 동의하지 않아도 기본 서비스 이용에는 제한이 없습니다.
+            </p>
+          </div>
+        </div>
+        <div class="modal-footer">
+          <button class="btn-disagree" @click="disagreeThirdParty">미동의</button>
+          <button class="btn-agree" @click="agreeThirdParty">동의</button>
+        </div>
+      </div>
+    </div>
   </div>
 </template>
 <script setup>
@@ -313,6 +394,18 @@
   const titleh = ref("인플루언서");
   const useStore = useAuthStore();
   const randomString = ref("");
+  
+  // 모달 상태
+  const showPrivacyModal = ref(false);
+  const showThirdPartyModal = ref(false);
+  
+  // 중복확인 상태
+  const isIdChecked = ref(false);
+  const isIdCheckPassedInfluencer = ref(false);
+  const isIdCheckPassedVendor = ref(false);
+  
+  // 비밀번호 표시/숨김 상태
+  const visible = ref(false);
 
   //인플루언서 폼
   const form = ref({
@@ -322,14 +415,19 @@
     formValue3: "", // 패스워드 확인
     formValue4: "", // 닉네임
     formValue5: useStore.getSnsTempData?.NAME || "", // 이름
-    formValue6: "소셜채널 선택", // 소셜 채널
+    formValue6: null, // 소셜 채널
     formValueItems6: [
+      {
+        text: "소셜 채널 선택",
+        value: null,
+        disabled: true,
+      },
       {
         text: "유튜브",
         value: "youtube",
       },
       {
-        text: "인스타",
+        text: "인스타그램",
         value: "instagram",
       },
       {
@@ -349,29 +447,7 @@
     formValue8: "010", // 휴대폰1
     formValue9: "", // 휴대폰2
     formValue10: "", // 휴대폰3
-    formValue11: "email", // 이메일1
-    formValueItems11: [
-      {
-        text: "이메일 선택",
-        value: "email",
-      },
-      {
-        text: "직접입력",
-        value: "direct",
-      },
-      {
-        text: "naver.com",
-        value: "naver",
-      },
-      {
-        text: "gmail.com",
-        value: "gmail",
-      },
-      {
-        text: "daum.net",
-        value: "daum",
-      },
-    ], // 이메일 아이템
+    formValue11: null, // 이메일 도메인 (combobox)
     formValue12: "", // 이메일2
     formValue13: "", // 자기소개
   });
@@ -384,34 +460,12 @@
     formValue3: "", // 패스워드 확인
     formValue4: "", // 닉네임
     formValue5: "", // 이름
-    formValue6: "소셜채널 선택", // 소셜 채널
+    formValue6: "", // 소셜 채널
     formValue7: "", // 소셜 ID 또는 주소
     formValue8: "010", // 휴대폰1
     formValue9: "", // 휴대폰2
     formValue10: "", // 휴대폰3
-    formValue11: "email", // 이메일1
-    formValueItems11: [
-      {
-        text: "이메일 선택",
-        value: "email",
-      },
-      {
-        text: "직접입력",
-        value: "direct",
-      },
-      {
-        text: "naver.com",
-        value: "naver",
-      },
-      {
-        text: "gmail.com",
-        value: "gmail",
-      },
-      {
-        text: "daum.net",
-        value: "daum",
-      },
-    ], // 이메일 아이템
+    formValue11: null, // 이메일 도메인 (combobox)
     formValue12: "",
     formValue13: "", // 개인정보약관동의
     formValue14: "", // 제3자 정보동의
@@ -431,6 +485,11 @@
     return result;
   };
 
+  // 비밀번호 표시/숨김 토글
+  const toggleVisibility = () => {
+    visible.value = !visible.value;
+  };
+
   const checkId = async () => {
     if (!form.value.formValue1) {
       $toast.error("아이디를 입력해주세요.");
@@ -446,18 +505,21 @@
 
     try {
       const response = await useAxios().post("/auth/checkId", {
-        ID: form.value.formValue1,
-        TYPE: "influence",
+        id: form.value.formValue1,
+        type: "influence",
       });
 
       if (response.data.isDuplicate) {
         $toast.error("이미 사용중인 아이디입니다.");
+        isIdCheckPassedInfluencer.value = false;
       } else {
         $toast.success("사용 가능한 아이디입니다.");
+        isIdCheckPassedInfluencer.value = true;
       }
     } catch (error) {
       console.error("ID check error:", error);
       $toast.error("아이디 중복 확인 중 오류가 발생했습니다.");
+      isIdCheckPassedInfluencer.value = false;
     }
   };
 
@@ -476,41 +538,125 @@
 
     try {
       const response = await useAxios().post("/auth/checkId", {
-        ID: formVendor.value.formValue1,
-        TYPE: typeParam.value,
+        id: formVendor.value.formValue1,
+        type: typeParam.value,
       });
 
       if (response.data.isDuplicate) {
         $toast.error("이미 사용중인 아이디입니다.");
+        isIdCheckPassedVendor.value = false;
       } else {
         $toast.success("사용 가능한 아이디입니다.");
+        isIdCheckPassedVendor.value = true;
       }
     } catch (error) {
       console.error("ID check error:", error);
       $toast.error("아이디 중복 확인 중 오류가 발생했습니다.");
+      isIdCheckPassedVendor.value = false;
     }
   };
-  // 회원가입 전에 아이디 유효성 검사 추가
+  // 회원가입 전에 유효성 검사
   const joinMember = async (id_type) => {
-    if (!useStore.getSnsTempData?.ID) {
-      if (id_type === "vendor") {
-        if (!formVendor.value.formValue1) {
-          $toast.error("아이디를 입력해주세요.");
-          return;
-        }
-        // const idRegex = /^[a-zA-Z0-9]{6,20}$/;
-        // if (!idRegex.test(form.value.formValue1)) {
-        //   $toast.error("아이디는 영문, 숫자 조합 6~20자로 입력해주세요.");
-        //   return;
-        // }
-      } else {
-        if (!formVendor.value.formValue1) {
-          $toast.error("아이디를 입력해주세요.");
-          return;
-        }
+    // 필수값 검사
+    if (id_type === "influence") {
+      // 인플루언서 필수값 검사
+      if (!form.value.formValue1) {
+        $toast.error("아이디를 입력해주세요.");
+        return;
+      }
+      if (!form.value.formValue2) {
+        $toast.error("비밀번호를 입력해주세요.");
+        return;
+      }
+      if (!form.value.formValue3) {
+        $toast.error("비밀번호 확인을 입력해주세요.");
+        return;
+      }
+      if (form.value.formValue2 !== form.value.formValue3) {
+        $toast.error("비밀번호가 일치하지 않습니다.");
+        return;
+      }
+      if (!form.value.formValue4) {
+        $toast.error("닉네임을 입력해주세요.");
+        return;
+      }
+      if (!form.value.formValue5) {
+        $toast.error("이름을 입력해주세요.");
+        return;
+      }
+      if (!form.value.formValue8 || !form.value.formValue9 || !form.value.formValue10) {
+        $toast.error("연락처를 입력해주세요.");
+        return;
+      }
+      if (!form.value.formValue12) {
+        $toast.error("이메일을 입력해주세요.");
+        return;
+      }
+      
+      // 개인정보약관동의 필수 체크
+      if (!formVendor.value.formValue13) {
+        $toast.error("개인정보약관동의는 필수입니다.");
+        return;
+      }
+      
+      // SNS 가입이 아닌 경우에만 아이디 중복확인 체크
+      if (!useStore.getSnsTempData?.ID && !isIdCheckPassedInfluencer.value) {
+        $toast.error("아이디 중복확인을 먼저 해주세요.");
+        return;
+      }
+    } else if (id_type === "vendor" || id_type === "brand") {
+      // 벤더/브랜드 필수값 검사
+      if (!formVendor.value.formValue1) {
+        $toast.error("아이디를 입력해주세요.");
+        return;
+      }
+      if (!formVendor.value.formValue2) {
+        $toast.error("비밀번호를 입력해주세요.");
+        return;
+      }
+      if (!formVendor.value.formValue3) {
+        $toast.error("비밀번호 확인을 입력해주세요.");
+        return;
+      }
+      if (formVendor.value.formValue2 !== formVendor.value.formValue3) {
+        $toast.error("비밀번호가 일치하지 않습니다.");
+        return;
+      }
+      if (!formVendor.value.formValue4) {
+        $toast.error("회사명을 입력해주세요.");
+        return;
+      }
+      if (!formVendor.value.formValue5) {
+        $toast.error("담당자명을 입력해주세요.");
+        return;
+      }
+      if (!formVendor.value.formValue8 || !formVendor.value.formValue9 || !formVendor.value.formValue10) {
+        $toast.error("연락처를 입력해주세요.");
+        return;
+      }
+      if (!formVendor.value.formValue12) {
+        $toast.error("이메일을 입력해주세요.");
+        return;
+      }
+      
+      // 개인정보약관동의 필수 체크
+      if (!formVendor.value.formValue13) {
+        $toast.error("개인정보약관동의는 필수입니다.");
+        return;
+      }
+      
+      // SNS 가입이 아닌 경우에만 아이디 중복확인 체크
+      if (!useStore.getSnsTempData?.ID && !isIdCheckPassedVendor.value) {
+        $toast.error("아이디 중복확인을 먼저 해주세요.");
+        return;
       }
     }
 
+    // 회원가입 확인 팝업
+    if (!confirm("회원가입을 진행하시겠습니까?")) {
+      return;
+    }
+
     let _req = "";
     let _api = "";
     if (id_type === "influence") {
@@ -521,10 +667,12 @@
         NAME: form.value.formValue5,
         NICK_NAME: form.value.formValue4 || "", //닉네임 없으면 빈문자
         PHONE: `${form.value.formValue8}-${form.value.formValue9}-${form.value.formValue10}`,
-        EMAIL: form.value.formValue12,
+        EMAIL: `${form.value.formValue12}@${form.value.formValue11 || ''}`,
         SNS_TYPE: form.value.formValue6,
         SNS_LINK_ID: form.value.formValue7,
         ADD_INFO1: form.value.formValue13,
+        PRIVACY_AGREE: formVendor.value.formValue13 ? "Y" : "N", // 개인정보약관동의
+        THIRDPARTY_AGREE: formVendor.value.formValue14 ? "Y" : "N", // 제3자 정보동의
         GOOGLE_REFRESH_TOKEN: useStore.getSnsTempData?.GOOGLE_REFRESH_TOKEN || "",
         TYPE: useStore.getSnsTempData ? "1" : "0", // SNS 가입일경우 1, 일반회원 가입일경우 0
       };
@@ -537,7 +685,9 @@
         COMPANY_NUMBER: (randomString.value = generateRandomAlphanumeric(100)),
         NAME: formVendor.value.formValue5,
         HP: `${formVendor.value.formValue8}-${formVendor.value.formValue9}-${formVendor.value.formValue10}`,
-        EMAIL: formVendor.value.formValue12,
+        EMAIL: `${formVendor.value.formValue12}@${formVendor.value.formValue11 || ''}`,
+        PRIVACY_AGREE: formVendor.value.formValue13 ? "Y" : "N", // 개인정보약관동의
+        THIRDPARTY_AGREE: formVendor.value.formValue14 ? "Y" : "N", // 제3자 정보동의
       }
     } else {
       _api = "/auth/joinbrand";
@@ -548,24 +698,30 @@
         COMPANY_NUMBER: (randomString.value = generateRandomAlphanumeric(100)),
         NAME: formVendor.value.formValue5,
         HP: `${formVendor.value.formValue8}-${formVendor.value.formValue9}-${formVendor.value.formValue10}`,
-        EMAIL: formVendor.value.formValue12,
+        EMAIL: `${formVendor.value.formValue12}@${formVendor.value.formValue11 || ''}`,
+        PRIVACY_AGREE: formVendor.value.formValue13 ? "Y" : "N", // 개인정보약관동의
+        THIRDPARTY_AGREE: formVendor.value.formValue14 ? "Y" : "N", // 제3자 정보동의
       };
     }
-
+    
     useAxios()
       .post(_api, _req)
       .then((res) => {
+        $toast.success("회원가입이 완료되었습니다!");
+        
         if (_req.TYPE === "1") {
           // SNS 가입일 경우
           useStore.setTempData("");
-          useUtil.setPageMove("/?type=influence");
+          setTimeout(() => {
+            //useUtil.setPageMove("/");
+          }, 1500);
           return;
         }
-        if (form.value.formValue0 === "Y") {
-          useUtil.setPageMove("/?type=influence");
-        } else {
-          useUtil.setPageMove("/?type=vendor");
-        }
+        
+        // 일반 회원가입 완료 후 메인페이지로 이동
+        setTimeout(() => {
+          //useUtil.setPageMove("/");
+        }, 1500);
       })
       .catch((error) => {
         if (error.response) {
@@ -588,6 +744,56 @@
       });
   };
 
+  // 개인정보 약관 모달 함수들
+  const handlePrivacyModal = (event) => {
+    console.log('개인정보 약관 클릭됨');
+    event.preventDefault();
+    showPrivacyModal.value = true;
+    // 체크 상태 초기화 (모달에서 동의해야만 체크됨)
+    formVendor.value.formValue13 = false;
+  };
+
+  const closePrivacyModal = () => {
+    showPrivacyModal.value = false;
+  };
+
+  const agreePrivacy = () => {
+    formVendor.value.formValue13 = true;
+    showPrivacyModal.value = false;
+    $toast.success('개인정보 수집 및 이용에 동의했습니다.');
+  };
+
+  const disagreePrivacy = () => {
+    formVendor.value.formValue13 = false;
+    showPrivacyModal.value = false;
+    $toast.info('개인정보 수집 및 이용에 동의하지 않았습니다.');
+  };
+
+  // 제3자 정보 제공 모달 함수들
+  const handleThirdPartyModal = (event) => {
+    console.log('제3자 정보 제공 클릭됨');
+    event.preventDefault();
+    showThirdPartyModal.value = true;
+    // 체크 상태 초기화 (모달에서 동의해야만 체크됨)
+    formVendor.value.formValue14 = false;
+  };
+
+  const closeThirdPartyModal = () => {
+    showThirdPartyModal.value = false;
+  };
+
+  const agreeThirdParty = () => {
+    formVendor.value.formValue14 = true;
+    showThirdPartyModal.value = false;
+    $toast.success('제3자 정보 제공에 동의했습니다.');
+  };
+
+  const disagreeThirdParty = () => {
+    formVendor.value.formValue14 = false;
+    showThirdPartyModal.value = false;
+    $toast.info('제3자 정보 제공에 동의하지 않았습니다.');
+  };
+
   /************************
    *    반응형 변수
    ************************/
@@ -597,6 +803,15 @@
   /************************
    *    마운트
    ************************/
+  // 아이디 변경 시 중복확인 상태 초기화
+  watch(() => form.value.formValue1, () => {
+    isIdCheckPassedInfluencer.value = false;
+  });
+
+  watch(() => formVendor.value.formValue1, () => {
+    isIdCheckPassedVendor.value = false;
+  });
+
   onMounted(() => {
     if(typeParam.value === "influence"){
       (form.value.formValue0 = "Y");
@@ -615,3 +830,134 @@
     }
   });
 </script>
+
+<style scoped>
+/* 모달 스타일 */
+.modal-overlay {
+  position: fixed;
+  top: 0;
+  left: 0;
+  right: 0;
+  bottom: 0;
+  background: rgba(0, 0, 0, 0.5);
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  z-index: 1000;
+}
+
+.modal-content {
+  background: white;
+  border-radius: 12px;
+  width: 90%;
+  max-width: 600px;
+  max-height: 80vh;
+  overflow: hidden;
+  box-shadow: 0 10px 25px rgba(0, 0, 0, 0.2);
+}
+
+.modal-header {
+  display: flex;
+  justify-content: space-between;
+  align-items: center;
+  padding: 20px 24px;
+  border-bottom: 1px solid #e0e0e0;
+  background: #f8f9fa;
+}
+
+.modal-header h3 {
+  margin: 0;
+  color: #333;
+  font-size: 1.2rem;
+}
+
+.modal-close-btn {
+  background: none;
+  border: none;
+  font-size: 24px;
+  cursor: pointer;
+  color: #666;
+  padding: 0;
+  width: 30px;
+  height: 30px;
+  display: flex;
+  align-items: center;
+  justify-content: center;
+}
+
+.modal-close-btn:hover {
+  color: #333;
+}
+
+.modal-body {
+  padding: 24px;
+  max-height: 50vh;
+  overflow-y: auto;
+}
+
+.privacy-content h4 {
+  margin: 20px 0 8px 0;
+  color: #333;
+  font-size: 1rem;
+  font-weight: 600;
+}
+
+.privacy-content h4:first-child {
+  margin-top: 0;
+}
+
+.privacy-content p {
+  margin: 8px 0;
+  color: #666;
+  line-height: 1.5;
+}
+
+.privacy-notice {
+  background: #f8f9fa;
+  padding: 12px;
+  border-radius: 6px;
+  margin-top: 20px;
+  font-size: 14px;
+  color: #555;
+}
+
+.modal-footer {
+  display: flex;
+  gap: 12px;
+  padding: 20px 24px;
+  border-top: 1px solid #e0e0e0;
+  background: #f8f9fa;
+  justify-content: flex-end;
+}
+
+.btn-agree {
+  background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
+  color: white;
+  border: none;
+  padding: 12px 24px;
+  border-radius: 8px;
+  cursor: pointer;
+  font-weight: 500;
+  transition: all 0.3s ease;
+}
+
+.btn-agree:hover {
+  transform: translateY(-2px);
+  box-shadow: 0 4px 12px rgba(102, 126, 234, 0.3);
+}
+
+.btn-disagree {
+  background: #f5f5f5;
+  color: #666;
+  border: none;
+  padding: 12px 24px;
+  border-radius: 8px;
+  cursor: pointer;
+  font-weight: 500;
+  transition: all 0.3s ease;
+}
+
+.btn-disagree:hover {
+  background: #e0e0e0;
+}
+</style>

+ 204 - 92
pages/view/common/mypage/index.vue

@@ -25,11 +25,15 @@
           <div v-if="!isEditMode" class="info-display">
             <div class="info-row">
               <label>이름</label>
-              <span>{{ userInfo.name }}</span>
+              <span>{{ myInfo.name }}</span>
+            </div>
+            <div class="info-row" v-if="userInfo.memberType === 'INFLUENCER'">
+              <label>닉네임</label>
+              <span>{{ myInfo.nickName }}</span>
             </div>
             <div class="info-row">
               <label>이메일</label>
-              <span>{{ userInfo.email }}</span>
+              <span>{{ myInfo.email }}</span>
             </div>
             <div class="info-row" v-if="userInfo.memberType !== 'INFLUENCER'">
               <label>회사명</label>
@@ -37,55 +41,103 @@
             </div>
             <div class="info-row">
               <label>전화번호</label>
-              <span>{{ userInfo.phone || '미등록' }}</span>
+              <span>{{ myInfo.phone || '미등록' }}</span>
+            </div>
+            <div class="info-row">
+              <label>SNS</label>
+              <span>{{ myInfo.sns_type || '미등록' }} {{ myInfo.sns_link || '미등록' }}</span>
             </div>
           </div>
 
           <div v-else class="info-edit">
-            <div class="form-group">
-              <label>이름 *</label>
+            <div class="info-row">
+              <label>이름</label>
+              <span>{{ myInfo.name }}</span>
+            </div>
+            <div class="info-row" v-if="userInfo.memberType === 'INFLUENCER'">
+              <label>닉네임</label>
               <input 
                 type="text" 
-                v-model="editForm.name"
-                placeholder="이름을 입력하세요"
-                class="form-input"
+                v-model="editForm.nickName"
+                placeholder="닉네임을 입력하세요"
+                class="edit-input w--20"
+              />
+            </div>
+            <div class="info-row">
+              <label>비밀번호 변경</label>
+              <input 
+                type="password" 
+                v-model="editForm.newPassword"
+                placeholder="비밀번호를 입력하세요."
+                class="edit-input"
               />
             </div>
-            <div class="form-group">
-              <label>이메일 *</label>
+            
+            <div class="info-row" v-if="editForm.newPassword">
+              <label>비밀번호 확인</label>
+              <input 
+                type="password" 
+                v-model="editForm.confirmPassword"
+                placeholder="비밀번호를 한 번 더 입력하세요."
+                class="edit-input"
+              />
+            </div>
+            <div class="info-row">
+              <label>이메일</label>
               <input 
                 type="email" 
                 v-model="editForm.email"
                 placeholder="이메일을 입력하세요"
-                class="form-input"
+                class="edit-input"
               />
             </div>
-            <div class="form-group" v-if="userInfo.memberType !== 'INFLUENCER'">
+            <div class="info-row" v-if="userInfo.memberType !== 'INFLUENCER'">
               <label>회사명</label>
               <input 
                 type="text" 
                 v-model="editForm.companyName"
                 placeholder="회사명을 입력하세요"
-                class="form-input"
+                class="edit-input"
               />
             </div>
-            <div class="form-group">
+            <div class="info-row">
               <label>전화번호</label>
               <input 
                 type="tel" 
                 v-model="editForm.phone"
                 placeholder="전화번호를 입력하세요"
-                class="form-input"
+                class="edit-input"
               />
             </div>
+            <div class="info-row sns--row">
+              <label>SNS</label>
+              <select v-model="editForm.snsType" class="edit-input">
+                <option value="">선택하세요</option>
+                <option value="youtube">유튜브</option>
+                <option value="instagram">인스타그램</option>
+                <option value="naver">네이버 블로그</option>
+                <option value="tiktok">틱톡</option>
+                <option value="facebook">페이스북</option>
+                <option value="twitter">트위터</option>
+                <option value="blog">개인 블로그</option>
+              </select>
+              <div class="edit-input-container">
+                <input 
+                  type="text" 
+                  v-model="editForm.snsLink"
+                  :placeholder="getSnsPlaceholder(editForm.snsType)"
+                  class="edit-input"
+                />
+              </div>
+            </div>
+            
             
-            <div class="form-actions">
+            <div class="edit-actions">
               <button 
                 class="btn-save" 
-                @click="saveProfile"
-                :disabled="isSaving"
+                @click="fnUpdate"
               >
-                {{ isSaving ? '저장중...' : '저장' }}
+                저장
               </button>
               <button 
                 class="btn-cancel" 
@@ -105,7 +157,7 @@
         <h3>계정 정보</h3>
         <div class="info-grid">
           <div class="info-item">
-            <span class="info-label">회원 타입</span>
+            <span class="info-label">계정 타입</span>
             <span class="info-value">{{ getMemberTypeName(userInfo.memberType) }}</span>
           </div>
           <div class="info-item">
@@ -114,11 +166,12 @@
           </div>
           <div class="info-item">
             <span class="info-label">가입일</span>
-            <span class="info-value">{{ formatDate(userInfo.regDate) || '정보 없음' }}</span>
+            <span class="info-value">{{ formatDate(myInfo.regDate) || '정보 없음' }}</span>
           </div>
         </div>
       </div>
     </div>
+
   </div>
 </template>
 
@@ -149,6 +202,8 @@ import "@vuepic/vue-datepicker/dist/main.css";
  ************************************************************************/
   const { $toast, $log, $dayjs, $eventBus } = useNuxtApp();
   const router = useRouter();
+
+  const myInfo = ref({});
   
   // 사용자 정보
   const userInfo = ref({
@@ -164,12 +219,16 @@ import "@vuepic/vue-datepicker/dist/main.css";
 
   // 편집 모드
   const isEditMode = ref(false);
-  const isSaving = ref(false);
   const editForm = ref({
     name: '',
+    nickName: '',
     email: '',
     companyName: '',
-    phone: ''
+    phone: '',
+    snsType: '',
+    snsLink: '',
+    newPassword: '',
+    confirmPassword: ''
   });
 
   /* eslint-disable */
@@ -197,10 +256,15 @@ import "@vuepic/vue-datepicker/dist/main.css";
     } else {
       // 편집 시작 - 현재 값으로 폼 초기화
       editForm.value = {
-        name: userInfo.value.name,
-        email: userInfo.value.email,
-        companyName: userInfo.value.companyName,
-        phone: userInfo.value.phone
+        name: myInfo.value.name || userInfo.value.name,
+        nickName: myInfo.value.nickName || '',
+        email: myInfo.value.email || userInfo.value.email,
+        companyName: userInfo.value.companyName || '',
+        phone: myInfo.value.phone || userInfo.value.phone,
+        snsType: myInfo.value.sns_type || '',
+        snsLink: myInfo.value.sns_link || '',
+        newPassword: '',
+        confirmPassword: ''
       };
       isEditMode.value = true;
     }
@@ -211,61 +275,57 @@ import "@vuepic/vue-datepicker/dist/main.css";
     isEditMode.value = false;
   };
 
+
   // 프로필 저장
-  const saveProfile = async () => {
-    // 유효성 검사
-    if (!editForm.value.name.trim()) {
-      $toast.error('이름을 입력해주세요.');
+  const fnUpdate = async () => {
+    // 패스워드 유효성 검사
+    if (editForm.value.newPassword && editForm.value.newPassword !== editForm.value.confirmPassword) {
+      $toast.error('패스워드가 일치하지 않습니다.');
       return;
     }
-    if (!editForm.value.email.trim()) {
-      $toast.error('이메일을 입력해주세요.');
+
+    if (editForm.value.newPassword && editForm.value.newPassword.length < 6) {
+      $toast.error('패스워드는 최소 6자 이상이어야 합니다.');
       return;
     }
 
-    isSaving.value = true;
-    
-    try {
-      const updateData = {
-        USER_SEQ: useAtStore.auth.seq,
-        NAME: editForm.value.name,
-        EMAIL: editForm.value.email,
-        PHONE: editForm.value.phone
-      };
+    let req = {
+      MEMBER_TYPE: useAtStore.auth.memberType,
+      MEMBER_SEQ: useAtStore.auth.seq,
+      EMAIL: editForm.value.email,
+      PHONE: editForm.value.phone,
+      SNS_TYPE: editForm.value.snsType,
+      SNS_LINK_ID: editForm.value.snsLink
+    }
 
-      // 회사 정보는 벤더사, 브랜드사만
-      if (userInfo.value.memberType !== 'INFLUENCER') {
-        updateData.COMPANY_NAME = editForm.value.companyName;
-      }
+    // 패스워드가 입력된 경우에만 추가
+    if (editForm.value.newPassword.trim()) {
+      req.PASSWORD = editForm.value.newPassword;
+    }
 
-      // TODO: 실제 API 호출 (현재는 임시로 로컬 업데이트)
-      // const response = await useAxios().post('/user/update', updateData);
+    // 인플루언서인 경우 닉네임 추가
+    if (userInfo.value.memberType === 'INFLUENCER') {
+      req.NICK_NAME = editForm.value.nickName;
+    }
+
+    useAxios().post('/mypage/update', req).then(async (res) => {
+      // 로컬 데이터 업데이트
+      myInfo.value.email = editForm.value.email;
+      myInfo.value.phone = editForm.value.phone;
+      myInfo.value.sns_type = editForm.value.snsType;
+      myInfo.value.sns_link = editForm.value.snsLink;
       
-      // 임시로 로컬 데이터 업데이트
-      userInfo.value.name = editForm.value.name;
-      userInfo.value.email = editForm.value.email;
-      userInfo.value.phone = editForm.value.phone;
-      if (userInfo.value.memberType !== 'INFLUENCER') {
-        userInfo.value.companyName = editForm.value.companyName;
+      if (userInfo.value.memberType === 'INFLUENCER') {
+        myInfo.value.nickName = editForm.value.nickName;
       }
 
       // 스토어도 업데이트
-      useAtStore.auth.name = editForm.value.name;
       useAtStore.auth.email = editForm.value.email;
       useAtStore.auth.phone = editForm.value.phone;
-      if (userInfo.value.memberType !== 'INFLUENCER') {
-        useAtStore.auth.companyName = editForm.value.companyName;
-      }
 
       $toast.success('프로필이 성공적으로 수정되었습니다.');
       isEditMode.value = false;
-      
-    } catch (error) {
-      console.error('프로필 수정 실패:', error);
-      $toast.error('프로필 수정에 실패했습니다.');
-    } finally {
-      isSaving.value = false;
-    }
+    })
   };
 
   const formatDate = (dateStr) => {
@@ -277,9 +337,21 @@ import "@vuepic/vue-datepicker/dist/main.css";
     return `${year}.${month}.${day}`;
   };
 
-  /************************************************************************
-|    WATCH
-************************************************************************/
+  // SNS 입력 placeholder 반환
+  const getSnsPlaceholder = (snsType) => {
+    const placeholders = {
+      'youtube': '채널 ID 또는 전체 URL',
+      'instagram': '사용자명 또는 전체 URL',
+      'naver': '블로그 ID 또는 전체 URL',
+      'tiktok': '사용자명 또는 전체 URL',
+      'facebook': '페이지명 또는 전체 URL',
+      'twitter': '사용자명 또는 전체 URL',
+      'blog': '블로그 주소',
+      'kakao': '사용자명 또는 전체 URL'
+    };
+    return placeholders[snsType] || 'SNS 링크 또는 ID를 입력하세요';
+  };
+
   // 사용자 상세 정보 로드
   const fnDetail = () => {
     let req = {
@@ -287,27 +359,24 @@ import "@vuepic/vue-datepicker/dist/main.css";
       MEMBER_SEQ: useAtStore.auth.seq
     }
 
-    console.warn(req)
-
     useAxios()
       .post(`/mypage/detail`, req)
       .then((res) => {
-        console.error(res)
-        userInfo.value = {
-          id: userData.ID,
-          name: userData.NAME,
-          email: userData.EMAIL,
-          companyName: userData.COMPANY_NAME,
-          companyNumber: userData.COMPANY_NUMBER,
-          phone: userData.PHONE,
-          memberType: userData.MEMBER_TYPE,
-          nickName: userData.NICK_NAME,
-          introduction: userData.INTRODUCTION,
-          regDate: userData.REGDATE
-        };
+        myInfo.value.id = res.data.ID;
+        myInfo.value.name = res.data.NAME;
+        myInfo.value.nickName = res.data.NICK_NAME;
+        myInfo.value.phone = res.data.PHONE;
+        myInfo.value.sns_type = res.data.SNS_TYPE;
+        myInfo.value.sns_link = res.data.SNS_LINK_ID;
+        myInfo.value.email = res.data.EMAIL;
+        myInfo.value.regDate = res.data.REGDATE;
     })
   };
 
+  /************************************************************************
+  |    라이프사이클
+  ************************************************************************/
+
   onMounted(() => {
     fnDetail();
   });
@@ -415,7 +484,7 @@ import "@vuepic/vue-datepicker/dist/main.css";
 }
 
 .info-display label {
-  width: 120px;
+  width: 150px;
   font-weight: 600;
   color: #333;
 }
@@ -426,35 +495,76 @@ import "@vuepic/vue-datepicker/dist/main.css";
 }
 
 /* 편집 폼 스타일 */
-.form-group {
-  margin-bottom: 24px;
+.info-edit .info-row {
+  display: flex;
+  padding: 16px 0;
+  border-bottom: 1px solid #f0f0f0;
+  align-items: center;
 }
 
-.form-group label {
-  display: block;
-  margin-bottom: 8px;
+.info-edit .info-row.sns--row > .edit-input{
+  width: 20%;
+  flex: none;
+  margin-right: 20px;
+}
+
+.info-edit .info-row.sns--row .edit-input-container{
+  width: 100%;
+}
+
+.info-edit .info-row.sns--row .edit-input-container .edit-input{
+  width: 100%;
+}
+
+.info-edit .info-row:last-child {
+  border-bottom: none;
+}
+
+.info-edit label {
+  width: 150px;
   font-weight: 600;
   color: #333;
+  flex-shrink: 0;
 }
 
-.form-input {
+.edit-input {
   width: 100%;
   padding: 12px 16px;
   border: 2px solid #e0e0e0;
   border-radius: 8px;
   font-size: 1rem;
   transition: border-color 0.3s ease;
+  background: white;
+}
+
+.edit-input.w--20{
+  width: 20%;
 }
 
-.form-input:focus {
+.edit-input:focus {
   outline: none;
   border-color: #667eea;
 }
 
-.form-actions {
+.edit-input:disabled {
+  background: #f8f9fa;
+  color: #6c757d;
+  cursor: not-allowed;
+}
+
+.edit-help {
+  display: block;
+  margin-top: 4px;
+  font-size: 0.8rem;
+  color: #666;
+  font-style: italic;
+}
+
+.edit-actions {
   display: flex;
   gap: 12px;
   margin-top: 32px;
+  justify-content: flex-end;
 }
 
 .btn-save {
@@ -535,4 +645,6 @@ import "@vuepic/vue-datepicker/dist/main.css";
   color: #333;
   font-size: 1rem;
 }
+
+
 </style>

部分文件因文件數量過多而無法顯示