[Kakao API] 회원가입 로그인

2025. 4. 10. 19:20·Web

https://9na0.tistory.com/124

 

[Kakao API] 로그인 기초

https://developers.kakao.com/ [카카오 로그인 사전설정]들어가서 내애플리케이션 들어가서 만들고햄버거클릭 > 플랫폼 > WEB 플랫폼 등록 > 도메인 등록 http://localhost:8080아래 등록하러가기 클릭활성화

9na0.tistory.com

이거 설정 다 하고 오시오


Oracle 테이블

/*
kakao2.jsp 회원테이블
회원 테이블 (주소x) 
1. 고유값 
2. 아이디
3. 이름 
4. 닉네임 (카카오)
5. 패스워드 (암호화 - MD5)
6. 이메일  
7. 연락처 (휴대폰) - 숫자만
8. 가입 형태 - web, kakao, naver, google, facebook
9. 가입일자 
MCODE MJOIN 따로 둔 이유
카카오로 로그인해서 추가정보 입력해서 web가입으로 돌릴때 처음 로그인 플랫폼 출력시 필요 
*/

CREATE TABLE MEMBERSHIP (
MIDX NUMBER(3) NOT NULL, -- 어제 만든 시퀀스가 500까지라서 3으로 만듦 
MCODE INTEGER DEFAULT '1' NOT NULL,
MID NVARCHAR2(30) NOT NULL,	-- 플랫폼마다 id길이가 달라서 잘 생각하고 만들어야함
MNAME NCHAR(10) NOT NULL,
MNICK NVARCHAR2(100) NULL,	-- 페북닉네임이 유독 긺 / web 사용자는 닉없음 
MPASS NVARCHAR2(100) NOT NULL,
MEMAIL NVARCHAR2(50) NOT NULL,
MHP NCHAR(11) NOT NULL,
MJOIN NCHAR(10) NOT NULL,
CONSTRAINT JOINPART CHECK(MJOIN IN('WEB','KAKAO','NAVER')),
MDATE TIMESTAMP DEFAULT SYSDATE,
PRIMARY KEY(MIDX),
UNIQUE (MID),
UNIQUE (MHP),
UNIQUE (MEMAIL)
);

SELECT * FROM MEMBERSHIP;

 

mapper.xml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="mytld.mycompany.myapp.membership_mapper">

<!-- 
map 이용하여 하나의 쿼리문으로 조건에 맞는 sql이 작동 되도록 설정
 -->
<select id="id_info" resultType="membership_DTO" parameterType="Map">
select mid,mpass,mhp,memail from membership
<choose>
 	<when test="part == 'login'"> <!-- 로그인 -->
 	 <where> <!-- where 태그는 where절을 입력시키는 태그 -->
 	 	mid= #{mid} and mpass= #{mpass}
 	 </where>
 	</when>
 	<when test="part == 'myinfo'"> <!-- 자신의 정보확인 및 수정 (kakao) -->
 	 	where mid= #{mid}
 	</when>
 	<otherwise> <!-- 회원전체 리스트 -->
 		order by midx desc
 	</otherwise>
</choose>

</select>

<select id="id_row" resultType="String">
select count(*) as ctl from membership where mid=#{mid}
</select>

<insert id="join_insert">
insert into membership (MIDX,MCODE,MID,MNAME,MNICK,MPASS,MEMAIL,MHP,MJOIN,MDATE) 
values (MNO.NEXTVAL,#{MCODE},#{MID},#{MNAME},#{MNICK},#{MPASS},#{MEMAIL},#{MHP},#{MJOIN},SYSDATE)
</insert>

</mapper>

 

membership_mapper.java

package mytld.mycompany.myapp;

import java.util.List;

import org.apache.ibatis.annotations.Mapper;

@Mapper
public interface membership_mapper {
	public String id_row(String mid);
	public int join_insert(membership_DTO dto);
	public List<membership_DTO> id_info(String mid, String mpass);
}

 

membership_DAO.java

package mytld.mycompany.myapp;

import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.annotation.Resource;

import org.mybatis.spring.SqlSessionTemplate;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Repository;

@Repository("membership_DAO")
public class membership_DAO implements membership_mapper {

	@Resource(name="sqltemp")
	public SqlSessionTemplate st;
	
	Map<String, String> mp = null;
	private static final Logger logger = LoggerFactory.getLogger(membership_DAO.class);
	@Override
	public List<membership_DTO> id_info(String mid, String mpass) {
		//Map 생성하는 이유 : mapper.xml에서 choose을 사용하여 각 when(조건) 별로 query를 다르게 실행 시키기 위함
		this.mp = new HashMap<String, String>();
		if(mpass != "") {
			this.mp.put("part", "login");
			this.mp.put("mid", mid);
			this.mp.put("mpass", mpass);
		}
		else {
			this.mp.put("part", "myinfo");
			this.mp.put("mid", mid);
		}
		//해당 조건에 mapper값을 return받음
		List<membership_DTO> all = this.st.selectList("id_info",this.mp);
		return all;
	}
	

	@Override
	public String id_row(String mid) {
		String result = null;
		try {
			//id는 대문자 소문자를 구분한다.
			result = st.selectOne("id_row",mid);
		}catch (Exception e) {
			System.out.println(e);
		}
		return result;
	}
	
	
	@Override
	public int join_insert(membership_DTO dto) {
		int result = 0;
		result = st.insert("join_insert",dto);
		return result;
	}
}

 

join.jsp 회원가입페이지

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<%@page import="java.util.Date"%>
<%Date date = new Date();%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>간편회원가입</title>
</head>
<body>

<form id="frm" method="post" action="./joinok.do">
<input type="hidden" name="MCODE" value="1">	<!-- 1 : 자회사회원가입 / 2 : 카카오 / 3 : 네이버 -->
<input type="hidden" name="MJOIN" value="WEB">	<!-- WEB, KAKAO, NAVER -->
<input type="hidden" id="ck" value="">	<!-- 아이디 중복체크 확인 -->

아이디 : <input type="text" name="MID"><input type="button" value="중복췤" id="idck"><br>
이름 : <input type="text" name="MNAME"><br>
닉네임 : <input type="text" name="MNICK"><br>
패스워드 : <input type="password" name="MPASS"><br>
<!-- 패스워드확인생략 -->
이메일 : <input type="text" name="MEMAIL"><br>
연락처 : <input type="text" name="MHP"><br>

<input type="button" value="회원가입" id="join">
</form>

</body>
<script>
let kid = sessionStorage.getItem("mid");
let knick = sessionStorage.getItem("knick");
if(kid != null){	//!=""하면 안먹음 
	document.querySelector("#ck").value = "ok";
	document.querySelector("#idck").style.display = "none";
	frm.MCODE.value = "2";
	frm.MJOIN.value = "KAKAO";
	frm.MID.value = kid;
	frm.MID.readonly = true;
	frm.MNICK.value = knick;
	frm.MNICK.readonly = true;
	frm.MPASS.value = kid;
	frm.MPASS.readonly = true;
}
</script>

<script type="module">
import {member_ck} from "./join.js";

document.querySelector("#idck").addEventListener("click",function(){
new member_ck().ajax_idcheck();
});

document.querySelector("#join").addEventListener("click",function(){
   new member_ck().join_okpage();
});
</script>
</html>

 

join.js

export class member_ck{
	
	//아이디 체크
	ajax_idcheck(){
		let id = frm.MID.value.replaceAll(" ","");
		if(id==""){
			alert("아이디를 입력하셔야 합니다.");
		}else{
			fetch("./login_idck.do",{
			method : "POST",
			headers : {"content-type":"application/x-www-form-urlencoded"},
			body : "id=" + frm.MID.value
			}).then(function(aa){
				return aa.text();
			}).then(function(bb){
				//console.log(bb);
				if(bb == "ok"){
					alert("사용가능한 아이디 입니다.");
					frm.MID.readOnly = true;
					document.querySelector("#ck").value = "ok";
				}else{
					alert("이미 사용중인 아이디입니다.");
					frm.MID.value = "";
				}
			}).catch(function(error){
				console.log(error);	
			});
			
					
		}
	}
	
	//회원가입 
	join_okpage(){
		if(document.querySelector("#ck").value !="ok"){
			alert("아이디입력 및 아이디 중복체크를 하여야 합니다");
		}else if(frm.MPASS.value == ""){
			alert("패스워드를 입력하세요");
		}else if(frm.MEMAIL.value == ""){
			alert("이메일을 입력하세요");
		}
		else{
			frm.submit();	
		}
		 
	}	
	
}

 

kakao2.jsp 로그인페이지

<%@ page language="java" contentType="text/html; charset=UTF-8"
	pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>로그인및 외부 API 로그인 방식(Kakao)</title>
</head>
<body>

	<form id="frm" method="post" action="./ajax/web_loginok.do">
	<!-- code = 1일반로그인 2카카오로그인 -->
		<input type="hidden" name="code" value="1">
		<input type="hidden" name="kakao_id" value="">
		<input type="hidden" name="kakao_nicknm" value="">
		<!-- autocomplete 자동완성 해제 : 보안굿 -->
		 아이디 : <input type="text" name="mid" autocomplete="off"><br> 
		 패스워드 : <input type="password" name="mpass"><br> 
		 <input type="submit" value="로그인"><br>
		 <input type="checkbox" id="save_id">아이디 저장<br>
		 <!-- 
		 자동로그인 기능은 실제로그인이 한번 돼야 활성화됨
		 자동로그인은 Back-end가 세션스토리지가 아니라 로컬스토리지로 셋아이템으로 브라우저에 저장해야함 
		 아이디 저장은 Front-end가 로컬스토리지에 저장함		 
		 -->
	</form>
	
	<br>
	<br>
	<img src="./ajax/kakao_login.png" onclick="kakao_login()">
	<p id="token-result"></p>
</body>
<script src="https://t1.kakaocdn.net/kakao_js_sdk/v1/kakao.js"></script>
<script>
	Kakao.init('카카오키');
	function kakao_login() {
		// Kakao.Auth.login : 카카오 회원가입 및 로그인 페이지를 출력하는 함수 
		Kakao.Auth.login({
			//성공시 출력되는 형태 
			success : function(response) {	//response : 결과화면 (사실 여기서 안넣어도됨)
				Kakao.API.request({			//사용자 가입정보를 요청 
					url : '/v2/user/me',	// 사용자 정보 가져오기
					success : function(response) {	//API서버에서 가입정보를 가져옴 
						console.log(response);
						let id = response["id"];		//고유값 
						let nickname = response["kakao_account"]["profile"]["nickname"];		//카카오 닉네임
						frm.code.value = "2";
						frm.kakao_id.value = id;
						frm.kakao_nicknm.value = nickname;
						frm.submit();
					},
					fail : function(error) {
						console.log(error);
						console.log("카카오 API 접속 오류");
					}
				});
			},
			//API 키가 맞지 않을 경우 출력되는 함수 
			fail : function(error) {
				console.log(error);
				console.log("카카오 key 접속 오류 및 환경설정 오류");
			}
		});
	}
</script>
<script>
	
</script>
<script>
	//해당 페이지로 접속시 작동되는 함수 
	window.onload = function(){
		let userid = localStorage.getItem("userid");
		if(userid != null){	//아이디 저장기능 활성화 
			frm.mid.value = userid;
			document.querySelector("#save_id").checked = true;	//아이디 저장 체크박스 체크 
		}
	}

	//아이디 저장 
	document.querySelector("#save_id").addEventListener("click", function(e) {
		if(frm.mid.value == ""){	
			alert("아이디를 입력하셔야 해당 기능을 사용할 수 있습니다.");
			this.checked = false;	//체크안되게 만들기 
		}else{
			if(this.checked == true){
				localStorage.setItem("userid",frm.mid.value);
			}else{
				localStorage.clear();
			}
			
		}
	});

	// ECMA => submit 사용 시 return, return false가 없습니다.
	document.querySelector("#frm").addEventListener("submit", function(e) {
		e.preventDefault(); // 강제정지
		if (frm.mid.value == "") {
			alert("아이디를 입력하세요");
		} else if (frm.mpass.value == "") {
			alert("패스워드를 입력하세요");
		} else {
			frm.submit();
		}
	});
</script>
</html>

controller.java Spring 컨트롤러

package mytld.mycompany.myapp;

import java.io.PrintWriter;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.util.List;
import java.util.Locale;

import javax.annotation.Resource;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;

import org.apache.commons.dbcp.BasicDataSource;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.SessionAttribute;

@Controller
public class HomeController {

	private static final Logger logger = LoggerFactory.getLogger(HomeController.class);

	@Autowired
	BasicDataSource dbinfo;

	@Resource(name = "membership_DAO")
	membership_DAO dao;

	@Resource(name = "m_md5")
	m_md5 md5 = null;

	HttpSession session = null;

	PrintWriter pw = null;

	String msg = null;

	// kakao2.jsp에서 넘어옴
	// @Post도 받고 @Get도 받아야됨
	// @Post => 일반로그인, kakao api, @Get => kakao Script
	// 일반로그인 + Kakao Script Login => @RequestMapping
	// 일반로그인 + Kakao API => @PostMapping
	/*
	 * ServletRequest, ServletResponse => session 사용 불가능 HttpServletRequest,
	 * HttpServletResponse => session 사용가능
	 */
	@RequestMapping("/ajax/web_loginok.do")
	public String web_loginok( // DTO로 받아도됨
			@RequestParam(name = "code") String code, @RequestParam(name = "mid", required = false) String mid,
			@RequestParam(name = "mpass", required = false) String mpass,
			@RequestParam(name = "kakao_id", required = false) String kakao_id,
			@RequestParam(name = "kakao_nicknm", required = false) String kakao_nicknm, Model m,
			HttpServletRequest req) {
		
		this.session = req.getSession();

		if (code.equals("1")) { // 일반 로그인 처리

			String pw = this.md5.md5_pass(mpass); // 사용자가 입력한 값을 암호화하여 회신
			List<membership_DTO> all = this.dao.id_info(mid, pw);
			System.out.println(all.size());

			if (all.size() > 0) {
				// 해당 로그인시 아이디를 session으로 등록함
				this.session.setAttribute("mid", all.get(0).getMID());

				System.out.println("세션 MID: " + this.session.getAttribute("mid"));

//				this.msg = "alert('로그인하였습니다');location.href='./myinfo.do';";
				this.msg = "setTimeout(function() { alert('로그인하였습니다'); location.href='./myinfo.do'; }, 300);";

				this.logger.info("로그인확인");
			} else {
				this.msg = "alert('로그인 실패하였습니다');" + "history.go(-1);";
				this.logger.info("로그인미확인");
			}
			m.addAttribute("msg", msg);

		} else if (code.equals("2")) { // 카카오 로그인 처리
			List<membership_DTO> all = this.dao.id_info(kakao_id, "");
			if (all.size() > 0) {
				//해당 로그인시 아이디를 session으로 등록함
				this.session.setAttribute("mid", all.get(0).getMID());
				msg = "alert('로그인 하셨습니다.'); location.href='./myinfo.do';";
			} else {
				// sessionStorage를 이용하여 간편회원가입을 등록하려함
				// 단 닉네임일 경우 특수문자를 사용할 수있으므로 생성시 ''로 변수값을 적용하여 처리
				this.msg = "alert('카카오 사용자로 로그인시 간편회원가입이 필요합니다.');"
						+ "sessionStorage.setItem('mid','"+ kakao_id +"');"
						+ "sessionStorage.setItem('mnick','"+ kakao_nicknm +"');"
						+ "location.href='../join.jsp';";
			}
			m.addAttribute("msg", msg);
		}

		return "joinok";
	}

	// 아이디 체크
	@PostMapping("/login_idck.do")
	public String login_idck(@RequestParam(name = "id") String id, ServletResponse res) {
		String result = null;
		try {
//			this.logger.info(id);
			this.pw = res.getWriter();

			// 아이디 대소문자 구별함
			// toUpperCase : 대문자로
			// toLowerCase : 소문자로
			result = this.dao.id_row(id.toLowerCase());
//			this.logger.info(result);
			if (result == null || result.equals("0")) {
				this.pw.write("ok");
			} else {
				this.pw.write("no");
			}

		} catch (Exception e) {
			this.logger.info(e.toString());
		} finally {
			this.pw.close();
		}

		return null;
	}

	@PostMapping("/joinok.do")
	public String joinok(@ModelAttribute membership_DTO dto, Model m) {

		String pw = dto.getMPASS();
		dto.setMPASS(this.md5.md5_pass(pw)); // 암호화 후 setter 사용

		try {
			int result = this.dao.join_insert(dto);
			if (result > 0) {

				m.addAttribute("msg", "alert('정상적으로 회원가입이 완료되었습니다');" + "location.href='./kakao2.jsp';");
			} else {
				m.addAttribute("msg", "alert('회원가입이 완료되지 않았습니다.');" + "history.go(-1);");
			}
		} catch (Exception e) {
			this.logger.info(e.toString());
		}

		return null;
	}

	// 로그인 사용자 정보 출력하는 페이지
	@GetMapping("/ajax/myinfo.do")
	public String myinfo(@SessionAttribute("mid") String MID, Model m) {
		this.logger.info(MID);
		m.addAttribute("MID", MID);

		return "/myinfo";
	}

	// 로그아웃
	@GetMapping("/ajax/logout.do")
	public String logout(HttpServletRequest req, Model m) {
		this.session = req.getSession();
		this.session.invalidate();

		this.msg = "alert('로그아웃 완료'); location.href='../kakao2.jsp';";

		m.addAttribute("msg", this.msg);

		return "joinok";
	}

}

 

kakao.jsp 로그인페이지 (일반로그인 + 카카오로그인)

<%@ page language="java" contentType="text/html; charset=UTF-8"
	pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>로그인및 외부 API 로그인 방식(Kakao)</title>
</head>
<body>

	<form id="frm" method="post" action="./ajax/web_loginok.do">
	<!-- code = 1일반로그인 2카카오로그인 -->
		<input type="hidden" name="code" value="1">
		<input type="hidden" name="kakao_id" value="">
		<input type="hidden" name="kakao_nicknm" value="">
		
		 아이디 : <input type="text" name="mid"><br> 
		 패스워드 : <input type="password" name="mpass"><br> 
		 <input type="submit" value="로그인">
	</form>
	
	<br>
	<br>
	<img src="./ajax/kakao_login.png" onclick="kakao_login()">
	<p id="token-result"></p>
</body>
<script src="https://t1.kakaocdn.net/kakao_js_sdk/v1/kakao.js"></script>
<script>
	Kakao.init('카카오 앱 키');
	function kakao_login() {
		// Kakao.Auth.login : 카카오 회원가입 및 로그인 페이지를 출력하는 함수 
		Kakao.Auth.login({
			//성공시 출력되는 형태 
			success : function(response) {	//response : 결과화면 (사실 여기서 안넣어도됨)
				Kakao.API.request({			//사용자 가입정보를 요청 
					url : '/v2/user/me',	// 사용자 정보 가져오기
					success : function(response) {	//API서버에서 가입정보를 가져옴 
						console.log(response);
						let id = response["id"];		//고유값 
						let nickname = response["kakao_account"]["profile"]["nickname"];		//카카오 닉네임
						frm.code.value = "2";
						frm.kakao_id.value = id;
						frm.kakao_nicknm.value = nickname;
						frm.submit();
					},
					fail : function(error) {
						console.log(error);
						console.log("카카오 API 접속 오류");
					}
				});
			},
			//API 키가 맞지 않을 경우 출력되는 함수 
			fail : function(error) {
				console.log(error);
				console.log("카카오 key 접속 오류 및 환경설정 오류");
			}
		});
	}
</script>
<script>
	
</script>
<script>
	// ECMA => submit 사용 시 return, return false가 없습니다.
	document.querySelector("#frm").addEventListener("submit", function(e) {
		e.preventDefault(); // 강제정지
		if (frm.mid.value == "") {
			alert("아이디를 입력하세요");
		} else if (frm.mpass.value == "") {
			alert("패스워드를 입력하세요");
		} else {
			frm.submit();
		}
	});
</script>
</html>

 

joinok.jsp 스크립트출력페이지

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<script>
${msg}
</script>

 

myinfo.jsp 세션정보로 로그인회원의 정보 출력

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>회원정보</title>
</head>
<!-- a 태그에 javascript 함수를 호출 시 javascript: 단어를 입력 후 호출 -->
<body>
${sessionScope.mid}님 환영합니다. <a href="javascript:kakao_logout()">[로그아웃]</a>
</body>
<script src="https://t1.kakaocdn.net/kakao_js_sdk/v1/kakao.js"></script>
<script>
Kakao.init('카카오 앱 키');	//키발급된 번호
function kakao_logout(){
	if(!Kakao.Auth.getAccessToken()){
		location.href = './logout.do';
	}
	else{
		Kakao.Auth.setAccessToken(undefined);
		sessionStorage.clear();
		localStorage.clear();
		location.href = './logout.do';
	}
}
</script>
</html>

 


아 너무 힘들다 머리카락이 다 빠질거같다 ㅠㅠ 우애애앵 맛있는거 먹어야지 우애애애앵 ㅠㅠ 카카오로그인이 제일쉬운api라고요? 허허! ㅠ 거짓말이라고해줘 제발 우어어어엉 우애애앵 ㅜㅜ 나 맛있는거 먹어야겠다 빨간거 기름진거 

 

mysql할땐 그냥 냅다 소문자 갖다때렸는데

오라클하니까 대문자로 하려하니까 DTO쓸때 편하긴한데 이것저것 보면서 할때 소문자랑 대문자랑 메챠쿠챠 섞여버려서 

세션에서 대소문자 못가려버리고 그만 한시간동안 에러를 찾게되는데!!!!!! 두둥탁...!

그냥 되는 대로 하긴했는데 담부터는 각잡고 열심히 대문자로 써봐야겠다

 

그리고 이거 보는사람은 카카오 키 잘 본인거 넣어서 쓰길 바람...  왜 값이 안넘어오징 ㅠ 했음....

저작자표시 비영리 변경금지 (새창열림)
'Web' 카테고리의 다른 글
  • 부트스트랩, 닷홈, 파일질라
  • [Kakao API] 개인정보 수정 + Oracle TRIGGER
  • [Kakao API] 로그인 기초
  • [Spring] PutMapping + GetMapping 을 이용한 Oracle 데이터 입출력
9na0
9na0
응애
  • 9na0
    구나딩
    9na0
  • 전체
    오늘
    어제
    • 분류 전체보기 (165) N
      • Web (91) N
      • Java (28)
      • 데이터베이스 (12)
      • 세팅 (11)
      • 과제 (3)
      • 쪽지시험 (2)
      • 정보처리기사 (2)
      • 서버 (13)
  • 블로그 메뉴

    • 링크

      • 구깃
    • 공지사항

    • 인기 글

    • 태그

      net3
      exam1_1~10
      datalist
      file25_t
      java_io1~10
      io_dto
      macbook pro m4
      spring-boot
      ab1
      Oracle
      file25
      notice_writer
      re_java10
      net4
      net2
      file24
      re2
      net1
      net5~10
      noticewriteok
    • 최근 댓글

    • 최근 글

    • hELLO· Designed By정상우.v4.10.3
    9na0
    [Kakao API] 회원가입 로그인
    상단으로

    티스토리툴바