api 진행

This commit is contained in:
hehihoho3@gmail.com 2025-09-19 16:34:45 +09:00
parent cf486f8e76
commit c26b36b59f
12 changed files with 1671 additions and 2 deletions

View File

@ -25,13 +25,18 @@
<pattern>/sample_mjon/jsp_example_hstry_detail_r1.jsp</pattern>
<pattern>/sample_mjon/jsp_example_select_price_r1.jsp</pattern>
<pattern>/sample_mjon/jsp_example_start_form_r1.jsp</pattern>
<pattern>/sample_mjon/jsp_example_send_msg_form_r1.jsp</pattern>
<pattern>/sample_mjon/jsp_example_send_msgs_form_r1.jsp</pattern>
<pattern>/sample_mjon/jsp_example_hstry_form_r1.jsp</pattern>
<pattern>/sample_mjon/jsp_example_hstry_detail_form_r1.jsp</pattern>
<pattern>/sample_mjon/jsp_example_select_price_form_r1.jsp</pattern>
<pattern>/sample_mjon/jsp_example_start_form_r1.jsp</pattern>
<pattern>/sample_mjon/jsp_example_select_price_form_r1.jsp</pattern>
<pattern>/sample_mjon/jsp_example_send_ft_form_r1.jsp</pattern>
<pattern>/sample_mjon/jsp_example_send_at_form_r1.jsp</pattern>
<pattern>/sample_mjon/jsp_example_inqry_chnlid_form_r1.jsp</pattern>
<pattern>/sample_mjon/jsp_example_inqry_templates_list_form_r1.jsp</pattern>

View File

@ -0,0 +1,149 @@
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<script type="text/javascript" src="./jquery-3.5.0.js"></script>
<script type="text/javascript">
$(document).ready(function(){
//채널ID 조회 함수
$("#ajax_get_chnlid").click(function(){
//채널ID 조회 결과 데이터 수신 전 기존 데이터 초기화
$('#dynamicTbody').html("");
//채널ID 조회 API로 전송할 데이터
var searchWebParam = {
'p_mberId' : $('#mberId').val() //회원 아이디
, 'p_apiKey' : $('#apiKey').val() //api 키
};
//채널ID 조회 REST API를 Ajax로 이용하기 위한 호출
$.ajax({
url : "./jsp_example_inqry_chnlid_r1.jsp", //요청 URL
dataType : "json", //요청 값을 json으로 수신
async : false,
type : "POST", //POST 방식
data : searchWebParam,
success: function (returnData, status) {
console.log('returnData :: ', returnData);
if (returnData.resultCode=="0"){ //결과가 성공인 경우 결과값 노출
if(returnData.data && returnData.data.length > 0){
makeResult(returnData);
} else {
alert('조회 결과가 없습니다.');
}
} else{ //결과가 실패인 경우 원인 노출
alert(returnData.data.resultCode+" : "+returnData.data.msg);
}
},
error : function(request, status, error){ //에러가 발생한 경우 에러 노출
alert(request+"///"+error+"///"+status+"///"+"AJAX_ERROR");
console.log("AJAX_ERROR");
}
});
});
});
function makeResult(p_returnData){
var v_html = "";
try {
console.log('p_returnData.data.length : ', p_returnData.data.length);
for (var i=0; i<p_returnData.data.length; i++){
var data = p_returnData.data[i];
console.log('p_returnData.data['+i+'] : ', data);
v_html += "<tr>";
// v_html += "<td>" + (data.profileId || '') + "</td>";
v_html += "<td>" + (data.senderKey || '') + "</td>";
v_html += "<td>" + (data.phoneNumber || '') + "</td>";
v_html += "<td>" + (data.yellowId || '') + "</td>";
// v_html += "<td>" + (data.categoryName || '') + "</td>";
v_html += "<td>" + (data.frstRegistPnttm || '') + "</td>";
v_html += "</tr>";
}
} catch (error) {
console.error(error);
}
$('#dynamicTbody').html(v_html);
}
</script>
<div class="inner">
<div class="send_top">
<div class="mypage_content current" id="tab5_3">
<div class="heading">
<h2><a href="./jsp_example_start_form_r1.jsp">돌아가기</a></h2>
<br/>
<!-- 채널ID 조회 설명 및 입력 영역 시작 -->
<h2>알림톡 채널ID 조회(샘플-문자온)</h2>
*mberId와 accessKey값은 실제 서비스시에는 jsp_example_inqry_chnlid_r1.jsp 파일에 작성하여 사용하세요
<br/>(샘플 페이지에서는 월활한 테스트를 위해 파라미터 형식으로 제공합니다.)
<br/><br/><br/>
</div>
<div class="mem_cont_in">
<div class="input_list">
<!--
<div class="input_list_item">
<div class="input_left">*mberId
<input type="text" class="list_inputType1" id="mberId" maxlength="100" value="goodgkdus" size="100"/>
</div>
</div>
<div class="input_list_item">
<div class="input_left">*api key
<input type="text" class="list_inputType1" id="apiKey" maxlength="100" value="24cb8ec4ed7c16969d2ab2988dd2406ee2820" size="100"/>
</div>
</div>
-->
<!-- 채널ID 조회 설명 및 입력 영역 끝 -->
<div class="mem_btnWrap2">
&nbsp;<button type="button" class="mem_btn3" style="width: 100%;" id="ajax_get_chnlid" onclick="return false;">채널ID 조회</button>
</div>
</div>
</div>
<!-- 채널ID 조회 결과 테이블 -->
<div class="list_cont" id="listTab_1">
<table class="board_list">
<colgroup>
<!-- <col style="width: 150px;"> -->
<col style="width: 200px;">
<col style="width: 150px;">
<col style="width: 150px;">
<!-- <col style="width: 200px;"> -->
<col style="width: 200px;">
</colgroup>
<thead>
<tr>
<!-- <th scope="row">프로필ID</th> -->
<th scope="row">발신프로필키(SenderKey)</th>
<th scope="row">핸드폰번호</th>
<th scope="row">채널ID(@ID)</th>
<!-- <th scope="row">카테고리명</th> -->
<th scope="row">등록일자</th>
</tr>
</thead>
<tbody id="dynamicTbody">
<!-- 조회 결과가 여기에 표시됩니다 -->
</tbody>
</table>
</div>
</div>
</div>
</div>

View File

@ -0,0 +1,94 @@
<%@page import="java.io.InputStreamReader"%>
<%@page import="java.io.BufferedReader"%>
<%@page import="org.apache.http.HttpResponse"%>
<%@page import="org.apache.http.client.methods.HttpPost"%>
<%@page import="org.apache.http.impl.client.HttpClients"%>
<%@page import="org.apache.http.client.HttpClient"%>
<%@page import="org.apache.http.HttpEntity"%>
<%@page import="java.net.URLEncoder"%>
<%@page import="org.apache.http.entity.ContentType"%>
<%@page import="java.util.Iterator"%>
<%@page import="java.nio.charset.Charset"%>
<%@page import="org.apache.http.entity.mime.HttpMultipartMode"%>
<%@page import="org.apache.http.entity.mime.MultipartEntityBuilder"%>
<%@page import="java.util.HashMap"%>
<%@page import="java.util.Map"%>
<%@ page language="java" contentType="text/html; charset=utf-8" pageEncoding="utf-8"%>
<%
try{
//기본 설정값
final String encodingType = "UTF-8";
final String boundary = "____boundary____";
//회원아이디, APIKEY - 보안을 위해 실제 서비스 시에는 이곳에 mberId와 apiKey 값을 적어서 사용
//실제서비스용
final String mberId = "dudgusw"; //문자온 로그인 아이디
final String apiKey = "3429312e6a2c732188d4cc7d15d8a1baa01d8d91"; //발급받은 api key
//테스트용
//String mberId = request.getParameter("p_mberId"); //문자온 로그인 아이디
//String apiKey = request.getParameter("p_apiKey"); //발급받은 api key
/******************** 전송 요청 URL ********************/
final String apiUrl = "http://119.193.215.98:8087/api/kakao/inqry/chnlId"; //채널ID 조회 API URL
Map<String, String> params = new HashMap<String, String>();
//기본 전송 데이터
params.put("mberId", mberId); //회원 아이디
params.put("accessKey", apiKey); //인증키
//REST API 전송
String result = "";
MultipartEntityBuilder builder = MultipartEntityBuilder.create();
builder.setBoundary(boundary);
builder.setMode(HttpMultipartMode.BROWSER_COMPATIBLE);
builder.setCharset(Charset.forName(encodingType));
try{
for(Iterator<String> i = params.keySet().iterator(); i.hasNext();){
String key = i.next();
//전달값이 없는 경우 오류
try{
String value = params.get(key);
if(value != null) {
builder.addTextBody(key, value, ContentType.create("Multipart/related", encodingType));
}
}catch(Exception ex){
ex.printStackTrace();
}
}
}catch(Exception ex){
ex.printStackTrace();
}
HttpEntity entity = builder.build();
HttpClient client = HttpClients.createDefault();
HttpPost post = new HttpPost(apiUrl);
post.setEntity(entity);
HttpResponse res = client.execute(post);
if(res != null){
BufferedReader in = new BufferedReader(new InputStreamReader(res.getEntity().getContent(), encodingType));
String buffer = null;
while((buffer = in.readLine())!=null){
result += buffer;
}
in.close();
}
out.print(result);
}catch(Exception e){
out.print("{\"data\":{\"resultCode\":99,\"msg\":\"WRONG API METHOD\"}}");
e.printStackTrace();
}
/**************** 채널ID 조회 Response 예제 ******************/
/* "resultCode": 결과코드, "objectList": 채널 목록 */
/* "msg": 결과 메시지, "profileId": 프로필ID, "senderKey": 발신프로필키 */
/* "phoneNumber": 핸드폰번호, "yellowId": 채널ID, "categoryName": 카테고리명 */
%>

View File

@ -0,0 +1,333 @@
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<script type="text/javascript" src="./jquery-3.5.0.js"></script>
<script type="text/javascript">
$(document).ready(function(){
// URL 파라미터에서 templateCode와 senderKey 가져오기
var urlParams = new URLSearchParams(window.location.search);
var templateCode = urlParams.get('templateCode');
var senderKey = urlParams.get('senderKey');
var mberId = urlParams.get('mberId');
if(templateCode) {
$('#templateCode').val(decodeURIComponent(templateCode));
}
if(senderKey) {
$('#senderKey').val(decodeURIComponent(senderKey));
}
if(mberId) {
$('#mberId').val(decodeURIComponent(mberId));
}
// 페이지 로드 시 자동으로 상세 조회
console.log(' + templateCode : ', templateCode);
console.log(' + senderKey : ', senderKey);
console.log(' + mberId : ', mberId);
if(templateCode && senderKey && mberId) {
sendData();
}
});
function sendData(){
//템플릿 상세 조회 결과 데이터 수신 전 기존 데이터 초기화
$('#templateDetailInfo').html("");
//템플릿 상세 조회 API로 전송할 데이터
var searchWebParam = {
'p_mberId' : $('#mberId').val() //비즈 아이디
, 'p_apiKey' : $('#apiKey').val() //api 키
, 'p_senderKey' : $('#senderKey').val() //발신프로필키
, 'p_templateCode' : $('#templateCode').val() //템플릿코드
};
//템플릿 상세 조회 REST API를 Ajax로 이용하기 위한 호출
$.ajax({
url : "./jsp_example_inqry_templates_detail_r1.jsp", //요청 URL
dataType : "json", //요청 값을 json으로 수신
async : false,
type : "POST", //POST 방식
data : searchWebParam,
success: function (returnData, status) {
console.log('returnData :: ', returnData);
if (returnData.resultCode=="0"){ //결과가 성공인 경우 결과값 노출
if(returnData.data){
makeResult(returnData.data);
} else {
alert('조회 결과가 없습니다.');
}
} else{ //결과가 실패인 경우 원인 노출
alert(returnData.data.resultCode+" : "+returnData.data.msg);
}
},
error : function(request, status, error){ //에러가 발생한 경우 에러 노출
alert(request+"///"+error+"///"+status+"///"+"AJAX_ERROR");
console.log("AJAX_ERROR");
}
});
}
function makeResult(data){
// 안전 이스케이프/유틸
const esc = (s) => String(s ?? '').replace(/[&<>"']/g, m => ({'&':'&amp;','<':'&lt;','>':'&gt;','"':'&quot;',"'":'&#39;'}[m]));
const yn = (v) => (String(v).toLowerCase() === 'true' ? 'Y' : 'N');
const row = (k, v) => "<tr><td>"+k+"</td><td>"+v+"</td></tr>";
const cell= (v) => "<td>"+v+"</td>";
const nl2br = (s) => esc(s).replace(/\r?\n/g, "<br/>");
let html = "";
try{
html += "<div class='template-detail-container'>";
// ===== 기본 정보 =====
html += "<div class='section'>";
html += "<h3>기본 정보</h3>";
html += "<table class='detail-table'>";
html += row("템플릿 코드", esc(data.templateCode));
html += row("템플릿 명", esc(data.templateName));
html += row("메시지 타입", esc(data.templateMessageType)); // AD/BA/EX/MI
html += row("강조 타입", esc(data.templateEmphasizeType)); // IMAGE/TEXT/ITEM_LIST/NONE
html += row("부가 정보", esc(data.templateExtra || "")); // 없으면 빈칸
// 광고 여부: 응답에 templateAd 필드가 없고, 메시지 타입이 AD면 광고성으로 보이는게 일반적
html += row("광고 여부", (String(data.templateMessageType).toUpperCase()==='AD' ? 'Y' : 'N'));
html += row("상태", (function(s){
switch(String(s).toUpperCase()){
case 'A': return '정상(A)'; case 'S': return '중단(S)'; case 'R': return '등록(R)'; default: return esc(s||'');
}
})(data.status));
html += row("검수 상태", (function(s){
switch(String(s).toUpperCase()){
case 'APR': return '승인(APR)';
case 'REJ': return '반려(REJ)';
case 'REQ': return '요청(REQ)';
case 'REG': return '등록(REG)';
default: return esc(s||'');
}
})(data.inspectionStatus));
html += row("카테고리 코드", esc(data.categoryCode));
html += row("생성 일자", esc(data.createdAt)); // ← createdAt
html += row("수정 일자", esc(data.modifiedAt)); // ← modifiedAt
html += row("보안 여부", yn(data.securityFlag));
html += row("차단 여부", yn(data.block));
html += row("휴면 여부", yn(data.dormant));
html += "</table>";
html += "</div>";
// ===== 이미지 (IMAGE일 때만) =====
if (String(data.templateEmphasizeType).toUpperCase() === 'IMAGE' && data.templateImageUrl){
html += "<div class='section'>";
html += "<h3>템플릿 이미지</h3>";
html += "<div class='image-wrap'><img src='"+esc(data.templateImageUrl)+"' alt='"+esc(data.templateImageName||"")+"' /></div>";
html += "</div>";
}
// ===== 템플릿 본문 =====
if (data.templateContent){
html += "<div class='section'>";
html += "<h3>템플릿 본문</h3>";
html += "<div class='template-content'><pre>"+esc(data.templateContent)+"</pre></div>";
html += "</div>";
}
// ===== 버튼 정보 =====
if (Array.isArray(data.buttons) && data.buttons.length){
html += "<div class='section'>";
html += "<h3>버튼 정보</h3>";
html += "<table class='detail-table'>";
html += "<tr><th>순번</th><th>버튼명</th><th>버튼타입</th><th>URL모바일</th><th>URL PC</th></tr>";
data.buttons.forEach(function(b, i){
html += "<tr>";
html += cell(i+1);
html += cell(esc(b.name||''));
html += cell(esc(b.linkType||'')); // ← linkType
html += cell(esc(b.linkMo||'')); // ← linkMo
html += cell(esc(b.linkPc||'')); // ← linkPc
html += "</tr>";
});
html += "</table>";
html += "</div>";
}
// ===== 코멘트 =====
if (Array.isArray(data.comments) && data.comments.length){
html += "<div class='section'>";
html += "<h3>검수/코멘트</h3>";
html += "<table class='detail-table'>";
html += "<tr><th>상태</th><th>작성일시</th><th>내용</th></tr>";
data.comments.forEach(function(c){
html += "<tr>";
html += cell(esc(c.status||'')); // APR 등
html += cell(esc(c.createdAt||'')); // 코멘트 시간
html += cell(nl2br(c.content||'')); // 줄바꿈 처리
html += "</tr>";
});
html += "</table>";
html += "</div>";
}
html += "</div>"; // container
}catch(e){
console.error(e);
html = "<div class='error'>데이터 처리 중 오류가 발생했습니다.</div>";
}
$('#templateDetailInfo').html(html);
}
function getStatusText(status) {
switch(status) {
case 'A': return '정상';
case 'S': return '중단';
case 'R': return '등록';
default: return status || '';
}
}
</script>
<style>
.template-detail-container {
max-width: 1000px;
margin: 0 auto;
}
.section {
margin-bottom: 30px;
border: 1px solid #ddd;
border-radius: 5px;
padding: 20px;
}
.section h3 {
margin-top: 0;
margin-bottom: 15px;
color: #333;
border-bottom: 2px solid #007bff;
padding-bottom: 5px;
}
.detail-table {
width: 100%;
border-collapse: collapse;
}
.detail-table td, .detail-table th {
border: 1px solid #ddd;
padding: 8px;
text-align: left;
}
.detail-table th {
background-color: #f8f9fa;
font-weight: bold;
}
.detail-table td:first-child {
background-color: #f8f9fa;
font-weight: bold;
width: 200px;
}
.template-content {
background-color: #f8f9fa;
border: 1px solid #ddd;
border-radius: 3px;
padding: 15px;
}
.template-content pre {
margin: 0;
white-space: pre-wrap;
word-wrap: break-word;
font-family: monospace;
}
.error {
color: red;
font-weight: bold;
text-align: center;
padding: 20px;
}
</style>
<div class="inner">
<div class="send_top">
<div class="mypage_content current" id="tab5_3">
<div class="heading">
<h2><a href="javascript:history.back()">돌아가기</a></h2>
<br/>
<!-- 템플릿 상세 조회 설명 및 입력 영역 시작 -->
<h2>알림톡 템플릿 상세 조회(샘플-문자온)</h2>
*bizId와 apiKey값은 실제 서비스시에는 jsp_example_inqry_templates_detail_r1.jsp 파일에 작성하여 사용하세요
<br/>(샘플 페이지에서는 월활한 테스트를 위해 파라미터 형식으로 제공합니다.)
<br/><br/><br/>
</div>
<div class="mem_cont_in">
<div class="input_list">
<!--
<div class="input_list_item">
<div class="input_left">*bizId
<input type="text" class="list_inputType1" id="bizId" maxlength="100" value="your_biz_id" size="100"/>
</div>
</div>
<div class="input_list_item">
<div class="input_left">*api key
<input type="text" class="list_inputType1" id="apiKey" maxlength="100" value="your_api_key" size="100"/>
</div>
</div>
-->
<!-- 발신프로필키 -->
<div class="input_list_item">
<div class="input_left">*senderKey (발신프로필키)
<input type="text" class="list_inputType1" id="senderKey" maxlength="100" value="" size="100"/>
</div>
</div>
<!-- 템플릿코드 -->
<div class="input_list_item">
<div class="input_left">*templateCode (템플릿코드)
<input type="text" class="list_inputType1" id="templateCode" maxlength="100" value="" size="100"/>
</div>
</div>
<!-- 템플릿코드 -->
<div class="input_list_item">
<div class="input_left">*mberId (mberId)
<input type="text" class="list_inputType1" id="mberId" maxlength="100" value="" size="100"/>
</div>
</div>
<!-- 템플릿 상세 조회 설명 및 입력 영역 끝 -->
<div class="mem_btnWrap2">
&nbsp;<button type="button" class="mem_btn3" style="width: 100%;" id="ajax_get_template_detail" onclick="return false;">템플릿 상세 조회</button>
</div>
</div>
</div>
<!-- 템플릿 상세 조회 결과 영역 -->
<div class="mem_cont_in">
<div id="templateDetailInfo">
<!-- 상세 정보가 여기에 표시됩니다 -->
</div>
</div>
</div>
</div>
</div>

View File

@ -0,0 +1,109 @@
<%@page import="java.io.InputStreamReader"%>
<%@page import="java.io.BufferedReader"%>
<%@page import="org.apache.http.HttpResponse"%>
<%@page import="org.apache.http.client.methods.HttpPost"%>
<%@page import="org.apache.http.impl.client.HttpClients"%>
<%@page import="org.apache.http.client.HttpClient"%>
<%@page import="org.apache.http.HttpEntity"%>
<%@page import="java.net.URLEncoder"%>
<%@page import="org.apache.http.entity.ContentType"%>
<%@page import="java.util.Iterator"%>
<%@page import="java.nio.charset.Charset"%>
<%@page import="org.apache.http.entity.mime.HttpMultipartMode"%>
<%@page import="org.apache.http.entity.mime.MultipartEntityBuilder"%>
<%@page import="java.util.HashMap"%>
<%@page import="java.util.Map"%>
<%@ page language="java" contentType="text/html; charset=utf-8" pageEncoding="utf-8"%>
<%
try{
//기본 설정값
final String encodingType = "UTF-8";
final String boundary = "____boundary____";
//비즈 아이디, APIKEY - 보안을 위해 실제 서비스 시에는 이곳에 bizId와 apiKey 값을 적어서 사용
//실제서비스용
final String accessKey = "3429312e6a2c732188d4cc7d15d8a1baa01d8d91"; //발급받은 api key
//테스트용
//String bizId = request.getParameter("p_bizId"); //비즈 아이디
//String apiKey = request.getParameter("p_apiKey"); //발급받은 api key
/******************** 전송 요청 URL ********************/
// final String apiUrl = "http://119.193.215.98:8087/api/kakao/inqry/templates/detail"; //템플릿 상세 조회 API URL
final String apiUrl = "http://localhost:8088/api/kakao/inqry/templates/detail"; //템플릿 상세 조회 API URL
/******************** 전송 정보 ********************/
//필수 값
String p_senderKey = request.getParameter("p_senderKey"); //발신프로필키
String p_templateCode = request.getParameter("p_templateCode"); //템플릿코드
String p_mberId = request.getParameter("p_mberId"); //발신프로필키
Map<String, String> params = new HashMap<String, String>();
System.out.println("accessKey : "+ accessKey); //비즈 아이디
System.out.println("mberId : "+ p_mberId); //비즈 아이디
System.out.println("senderKey : "+ p_senderKey); //발신프로필키
System.out.println("templateCode : "+ p_templateCode); //템플릿코드
//기본 전송 데이터
params.put("accessKey", accessKey); //비즈 아이디
params.put("mberId", p_mberId); //비즈 아이디
params.put("senderKey", p_senderKey); //발신프로필키
params.put("templateCode", p_templateCode); //템플릿코드
//REST API 전송
String result = "";
MultipartEntityBuilder builder = MultipartEntityBuilder.create();
builder.setBoundary(boundary);
builder.setMode(HttpMultipartMode.BROWSER_COMPATIBLE);
builder.setCharset(Charset.forName(encodingType));
try{
for(Iterator<String> i = params.keySet().iterator(); i.hasNext();){
String key = i.next();
//전달값이 없는 경우 오류
try{
String value = params.get(key);
if(value != null) {
builder.addTextBody(key, value, ContentType.create("Multipart/related", encodingType));
}
}catch(Exception ex){
ex.printStackTrace();
}
}
}catch(Exception ex){
ex.printStackTrace();
}
HttpEntity entity = builder.build();
HttpClient client = HttpClients.createDefault();
HttpPost post = new HttpPost(apiUrl);
post.setEntity(entity);
HttpResponse res = client.execute(post);
if(res != null){
BufferedReader in = new BufferedReader(new InputStreamReader(res.getEntity().getContent(), encodingType));
String buffer = null;
while((buffer = in.readLine())!=null){
result += buffer;
}
in.close();
}
out.print(result);
}catch(Exception e){
out.print("{\"data\":{\"resultCode\":99,\"msg\":\"WRONG API METHOD\"}}");
e.printStackTrace();
}
/**************** 템플릿 상세 조회 Response 예제 ******************/
/* "resultCode": 결과코드, "objectList": 템플릿 상세 정보 */
/* "msg": 결과 메시지, "templateCode": 템플릿코드, "templateName": 템플릿명 */
/* "template": 템플릿 내용, "buttons": 버튼 정보, "quickReplies": 퀵 리플라이 */
%>

View File

@ -0,0 +1,182 @@
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<script type="text/javascript" src="./jquery-3.5.0.js"></script>
<script type="text/javascript">
$(document).ready(function(){
//템플릿 목록 조회 함수
$("#ajax_get_templates_list").click(function(){
//템플릿 목록 조회 결과 데이터 수신 전 기존 데이터 초기화
$('#dynamicTbody').html("");
//템플릿 목록 조회 API로 전송할 데이터
var searchWebParam = {
'p_mberId' : $('#mberId').val() //비즈 아이디
, 'p_accessKey' : $('#accessKey').val() //api 키
, 'p_senderKey' : $('#senderKey').val() //발신프로필키
};
//템플릿 목록 조회 REST API를 Ajax로 이용하기 위한 호출
$.ajax({
url : "./jsp_example_inqry_templates_list_r1.jsp", //요청 URL
dataType : "json", //요청 값을 json으로 수신
async : false,
type : "POST", //POST 방식
data : searchWebParam,
success: function (returnData, status) {
console.log('returnData :: ', returnData);
if (returnData.resultCode=="0"){ //결과가 성공인 경우 결과값 노출
if(returnData.data && returnData.data.length > 0){
makeResult(returnData);
} else {
alert('조회 결과가 없습니다.');
}
} else{ //결과가 실패인 경우 원인 노출
alert(returnData.resultCode+" : "+returnData.msg);
}
},
error : function(request, status, error){ //에러가 발생한 경우 에러 노출
alert(request+"///"+error+"///"+status+"///"+"AJAX_ERROR");
console.log("AJAX_ERROR");
}
});
});
});
function makeResult(p_returnData){
var v_html = "";
try {
for (var i=0; i<p_returnData.data.length; i++){
var data = p_returnData.data[i];
console.log('p_returnData.data['+i+'] : ', data);
v_html += "<tr>";
v_html += "<td>" + (data.templateCode || '') + "</td>";
v_html += "<td>" + (data.templateName || '') + "</td>";
v_html += "<td>";
v_html += getStatusText(data.serviceStatus);
v_html += "</td>";
v_html += "<td>" + (data.createdAt || '') + "</td>";
v_html += "<td>";
v_html += "<button type='button' class='mem_btn3' onclick='getTemplateDetail(\"" + (data.templateCode || '') + "\")'>상세조회</button>";
v_html += "</td>";
v_html += "</tr>";
}
} catch (error) {
console.error(error);
}
$('#dynamicTbody').html(v_html);
}
function getStatusText(status) {
switch(status) {
case 'A': return '정상';
case 'S': return '중단';
case 'R': return '등록';
default: return status || '';
}
}
function getTemplateDetail(templateCode) {
if(!templateCode) {
alert('템플릿 코드가 없습니다.');
return;
}
// 템플릿 상세 조회 페이지로 이동
var url = './jsp_example_inqry_templates_detail_form_r1.jsp?templateCode=' + encodeURIComponent(templateCode)
+ '&senderKey=' + encodeURIComponent($('#senderKey').val())
+ '&mberId=' + encodeURIComponent($('#mberId').val());
// window.open(url, '_blank', 'width=1200,height=800,scrollbars=yes');
// 방법 1: 현재 창에서 바로 이동
location.href = url;
// 방법 2: 현재 창 히스토리를 남기지 않고 이동
// window.location.replace(url);
// 방법 3: 완전히 새 탭(새창)으로 열고 싶으면
// window.open(url, '_blank'); // 이건 팝업 차단 옵션 따라 새탭/새창으로 뜸
}
</script>
<div class="inner">
<div class="send_top">
<div class="mypage_content current" id="tab5_3">
<div class="heading">
<h2><a href="./jsp_example_start_form_r1.jsp">돌아가기</a></h2>
<br/>
<!-- 템플릿 목록 조회 설명 및 입력 영역 시작 -->
<h2>알림톡 템플릿 목록 조회(샘플-문자온)</h2>
*apiKey값은 실제 서비스시에는 jsp_example_inqry_templates_list_r1.jsp 파일에 작성하여 사용하세요
<br/>(샘플 페이지에서는 월활한 테스트를 위해 파라미터 형식으로 제공합니다.)
<br/><br/><br/>
</div>
<div class="mem_cont_in">
<div class="input_list">
<!-- 발신프로필키 -->
<div class="input_list_item">
<div class="input_left">*senderKey (발신프로필키)
<input type="text" class="list_inputType1" id="senderKey" maxlength="100" value="your_sender_key" size="100"/>
</div>
<div class="input_left">*mberId (사용자ID)
<input type="text" class="list_inputType1" id="mberId" maxlength="100" value="your_mber_id" size="100"/>
</div>
</div>
<!-- 템플릿 목록 조회 설명 및 입력 영역 끝 -->
<div class="mem_btnWrap2">
&nbsp;<button type="button" class="mem_btn3" style="width: 100%;" id="ajax_get_templates_list" onclick="return false;">템플릿 목록 조회</button>
</div>
</div>
</div>
<!-- 템플릿 목록 조회 결과 테이블 -->
<div class="list_cont" id="listTab_1">
<table class="board_list">
<colgroup>
<col style="width: 200px;">
<col style="width: 250px;">
<col style="width: 80px;">
<col style="width: 250px;">
<col style="width: 100px;">
</colgroup>
<thead>
<tr>
<th scope="row">템플릿코드</th>
<th scope="row">템플릿명</th>
<th scope="row">상태</th>
<th scope="row">생성일자</th>
<th scope="row">상세조회</th>
</tr>
</thead>
<tbody id="dynamicTbody">
<!-- 조회 결과가 여기에 표시됩니다 -->
</tbody>
</table>
</div>
</div>
</div>
</div>

View File

@ -0,0 +1,102 @@
<%@page import="java.io.InputStreamReader"%>
<%@page import="java.io.BufferedReader"%>
<%@page import="org.apache.http.HttpResponse"%>
<%@page import="org.apache.http.client.methods.HttpPost"%>
<%@page import="org.apache.http.impl.client.HttpClients"%>
<%@page import="org.apache.http.client.HttpClient"%>
<%@page import="org.apache.http.HttpEntity"%>
<%@page import="java.net.URLEncoder"%>
<%@page import="org.apache.http.entity.ContentType"%>
<%@page import="java.util.Iterator"%>
<%@page import="java.nio.charset.Charset"%>
<%@page import="org.apache.http.entity.mime.HttpMultipartMode"%>
<%@page import="org.apache.http.entity.mime.MultipartEntityBuilder"%>
<%@page import="java.util.HashMap"%>
<%@page import="java.util.Map"%>
<%@ page language="java" contentType="text/html; charset=utf-8" pageEncoding="utf-8"%>
<%
try{
//기본 설정값
final String encodingType = "UTF-8";
final String boundary = "____boundary____";
//비즈 아이디, APIKEY - 보안을 위해 실제 서비스 시에는 이곳에 bizId와 apiKey 값을 적어서 사용
//실제서비스용
final String accessKey = "3429312e6a2c732188d4cc7d15d8a1baa01d8d91"; //발급받은 api key
//테스트용
//String bizId = request.getParameter("p_bizId"); //비즈 아이디
//String apiKey = request.getParameter("p_apiKey"); //발급받은 api key
/******************** 전송 요청 URL ********************/
// final String apiUrl = "http://119.193.215.98:8087/api/kakao/inqry/templates/list"; //템플릿 목록 조회 API URL
final String apiUrl = "http://localhost:8088/api/kakao/inqry/templates/list"; //템플릿 목록 조회 API URL
/******************** 전송 정보 ********************/
//필수 값
String p_senderKey = request.getParameter("p_senderKey"); //발신프로필키
String p_mberId = request.getParameter("p_mberId"); //발신프로필키
Map<String, String> params = new HashMap<String, String>();
System.out.println("accessKey : "+ accessKey);
//기본 전송 데이터
params.put("mberId", p_mberId); //비즈 아이디
params.put("accessKey", accessKey); //인증키
params.put("senderKey", p_senderKey); //발신프로필키
//REST API 전송
String result = "";
MultipartEntityBuilder builder = MultipartEntityBuilder.create();
builder.setBoundary(boundary);
builder.setMode(HttpMultipartMode.BROWSER_COMPATIBLE);
builder.setCharset(Charset.forName(encodingType));
try{
for(Iterator<String> i = params.keySet().iterator(); i.hasNext();){
String key = i.next();
//전달값이 없는 경우 오류
try{
String value = params.get(key);
if(value != null) {
builder.addTextBody(key, value, ContentType.create("Multipart/related", encodingType));
}
}catch(Exception ex){
ex.printStackTrace();
}
}
}catch(Exception ex){
ex.printStackTrace();
}
HttpEntity entity = builder.build();
HttpClient client = HttpClients.createDefault();
HttpPost post = new HttpPost(apiUrl);
post.setEntity(entity);
HttpResponse res = client.execute(post);
if(res != null){
BufferedReader in = new BufferedReader(new InputStreamReader(res.getEntity().getContent(), encodingType));
String buffer = null;
while((buffer = in.readLine())!=null){
result += buffer;
}
in.close();
}
out.print(result);
}catch(Exception e){
out.print("{\"data\":{\"resultCode\":99,\"msg\":\"WRONG API METHOD\"}}");
e.printStackTrace();
}
/**************** 템플릿 목록 조회 Response 예제 ******************/
/* "resultCode": 결과코드, "objectList": 템플릿 목록 */
/* "msg": 결과 메시지, "templateCode": 템플릿코드, "templateName": 템플릿명 */
/* "templateMessageType": 메시지타입, "status": 상태, "inspectionStatus": 검수상태 */
%>

View File

@ -0,0 +1,283 @@
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html lang="ko">
<head>
<meta charset="UTF-8">
<title>알림톡 발송(샘플-문자온)</title>
<script type="text/javascript" src="./jquery-3.5.0.js"></script>
<script type="text/javascript">
$(function(){
// ===== 전역 설정: multipart로 보낼지 여부 (기본: x-www-form-urlencoded) =====
var USE_MULTIPART = false; // 서버가 multipart/form-data만 받으면 true로 바꿔
// ===== 클릭 바인딩 (중복 방지) =====
$("#ajax_send_at").off("click").on("click", function(){
// 결과 초기화
$("#msgGroupId,#resultCode,#resultMsg,#successCnt,#failCnt").text("");
// 페이로드 생성
var payload = buildPayload();
console.log("[SEND PAYLOAD]", payload);
if (USE_MULTIPART){
// multipart/form-data
var fd = new FormData();
Object.keys(payload).forEach(function(k){ fd.append(k, payload[k]); });
$.ajax({
url: "./jsp_example_send_at_r1.jsp",
type: "POST",
data: fd,
processData: false,
contentType: false,
dataType: "json",
success: handleSuccess,
error: handleError
});
} else {
// x-www-form-urlencoded
$.ajax({
url: "./jsp_example_send_at_r1.jsp",
type: "POST",
data: payload,
dataType: "json",
success: handleSuccess,
error: handleError
});
}
});
// ===== 수신자 추가 =====
$("#addReceiver").off("click").on("click", function(){
var nextId = $(".varItem").length + 1;
var tpl = ''
+ '<fieldset class="varItem" style="margin:8px 0;">'
+ ' <legend>수신자 ' + nextId + '</legend>'
+ ' <table border="1" cellspacing="0" cellpadding="6" style="width:100%;">'
+ ' <colgroup><col style="width:180px"><col></colgroup>'
+ ' <tbody>'
+ ' <tr><th align="left">수신자번호</th>'
+ ' <td><input type="text" class="list_inputType1 callTo" size="30" value=""/></td></tr>'
+ ' <tr><th align="left">알림톡 내용 </th>'
+ ' <td><textarea class="list_inputType1 msgTxt" rows="3" cols="80"></textarea></td></tr>'
+ ' <tr><th align="left">알림톡 제목 (선택)</th>'
+ ' <td><input type="text" class="list_inputType1 title" size="60" value=""/></td></tr>'
+ ' <tr><th align="left">대체문자 본문 (선택)</th>'
+ ' <td><input type="text" class="list_inputType1 altSms" size="80" value=""/></td></tr>'
+ ' </tbody>'
+ ' </table>'
+ ' <div style="margin-top:6px;"><button type="button" class="mem_btn3 removeReceiver">삭제</button></div>'
+ '</fieldset>';
$("#receiverList").append(tpl);
});
// ===== 수신자 삭제 (델리게이션) =====
$(document).on("click", ".removeReceiver", function(){
$(this).closest(".varItem").remove();
renumberReceivers();
});
// ===== 페이로드 빌더 (Postman 스샷과 동일 키) =====
function buildPayload(){
var p = {
// 공통(단건) 필드
mberId : $("#mberId").val(), // 필요없으면 서버에서 무시
accessKey : $("#apiKey").val(), // accessKey = apiKey 인풋 재사용
senderKey : $("#senderKey").val(),
templateCode : $("#templateCode").val(),
subMsgSendYn : $("#subMsgSendYn").val(), // Y/N
callFrom : $("#callFrom").val()
// testYn 을 서버가 받는다면 여기에 testYn: $("#testYn").val() 추가
};
// 수신자별(N명) 필드
var idx = 1;
$(".varItem").each(function(){
var callTo = $(this).find(".callTo").val();
if (!callTo) return; // 번호 없으면 skip (인덱스 띄지 않도록 주의)
var title = $(this).find(".title").val();
var content = $(this).find(".msgTxt").val();
var altSms = $(this).find(".altSms").val(); // 없으면 빈값
// 스샷 규칙과 동일한 키로 세팅
p["callTo_" + idx] = callTo;
if (title) p["templateTitle_" + idx] = title;
if (content) p["templateContent_" + idx] = content;
if (altSms) p["subMsgTxt_" + idx] = altSms;
// 치환변수 (var1, var2 …)
$(this).find(".variable").each(function(){
var varName = $(this).data("var"); // 예: var1
var varValue = $(this).val();
if (varName && varValue){
p[varName + "_" + idx] = varValue; // 예: var1_1 = '홍길동'
}
});
idx++;
});
return p;
}
// ===== 성공/에러 공통 처리 =====
function handleSuccess(ret){
console.log("returnData :: ", ret);
if (ret && ret.data && ret.data.resultCode == "0"){
$("#msgGroupId").text(ret.data.msgGroupId || "");
$("#resultCode").text(ret.data.resultCode || "");
$("#resultMsg").text(ret.data.msg || "");
$("#successCnt").text(ret.data.successCnt || "");
$("#failCnt").text(ret.data.failCnt || "");
} else {
var rc = ret && ret.data ? ret.data.resultCode : "UNKNOWN";
var rm = ret && ret.data ? ret.data.msg : "오류";
$("#resultCode").text(rc);
$("#resultMsg").text(rm);
alert(rc + " : " + rm);
}
}
function handleError(req, status, err){
alert(req + "///" + err + "///" + status + "///AJAX_ERROR");
console.log("AJAX_ERROR", req, status, err);
}
// ===== 가독성용: 수신자 번호 레전드 재정렬 =====
function renumberReceivers(){
$(".varItem").each(function(i){
$(this).find("legend").text("수신자 " + (i+1));
});
}
});
</script>
</head>
<body>
<div class="inner">
<div class="send_top">
<div class="mypage_content current" id="tab5_3">
<div class="heading">
<h2><a href="./jsp_example_start_form_r1.jsp">돌아가기</a></h2>
<h2>알림톡 발송(샘플-문자온)</h2>
*mberId와 accessKey값은 실제 서비스시에는 <b>jsp_example_send_at_r1.jsp</b> 파일에 작성하여 사용하세요.<br/>
(샘플 페이지에서는 원활한 테스트를 위해 파라미터 형식으로 제공합니다.)
<br/><br/>
</div>
<!-- 기본 설정 -->
<fieldset>
<legend>알림톡 기본 설정</legend>
<table border="1" cellspacing="0" cellpadding="6" style="width:100%;">
<colgroup><col style="width:260px"><col></colgroup>
<tbody>
<!-- 필요 시 주석 해제해서 사용
<tr>
<th align="left">*mberId</th>
<td><input type="text" class="list_inputType1" id="mberId" size="40" value="your_biz_id" /></td>
</tr>
<tr>
<th align="left">*api key</th>
<td><input type="text" class="list_inputType1" id="apiKey" size="60" value="your_api_key" /></td>
</tr>
-->
<tr>
<th align="left">*senderKey (카카오 알림톡 채널ID)</th>
<td><input type="text" class="list_inputType1" id="senderKey" size="60" value="your_sender_key" /></td>
</tr>
<tr>
<th align="left">*templateCode (카카오 알림톡 템플릿 코드)</th>
<td><input type="text" class="list_inputType1" id="templateCode" size="60" value="your_template_code" /></td>
</tr>
<tr>
<th align="left">callFrom (발신번호)</th>
<td><input type="text" class="list_inputType1" id="callFrom" size="30" value="01093414986" /></td>
</tr>
<tr>
<th align="left">subMsgSendYn (대체문자 전송 여부)</th>
<td>
<input type="text" class="list_inputType1" id="subMsgSendYn" size="5" value="Y" />
<span>(Y-전송, N-전송안함)</span>
</td>
</tr>
<tr>
<th align="left">테스트여부</th>
<td>
<input type="text" class="list_inputType1" id="testYn" size="6" />
<span>(기본값 없음, YF-테스트(실패), YS-테스트(성공))</span>
</td>
</tr>
</tbody>
</table>
</fieldset>
<!-- 수신자 목록 -->
<fieldset style="margin-top:16px;">
<legend>수신자 목록</legend>
<div style="margin:8px 0;">
<button type="button" class="mem_btn3" id="addReceiver">수신자 추가</button>
</div>
<div id="receiverList">
<!-- 기본 수신자 1 -->
<fieldset class="varItem" style="margin:8px 0;">
<legend>수신자 1</legend>
<table border="1" cellspacing="0" cellpadding="6" style="width:100%;">
<colgroup><col style="width:180px"><col></colgroup>
<tbody>
<tr>
<th align="left">수신자번호</th>
<td><input type="text" class="list_inputType1 callTo" size="30" value="01000001111" /></td>
</tr>
<tr>
<th align="left">알림톡 내용</th>
<td><textarea class="list_inputType1 msgTxt" rows="3" cols="80">안녕하세요. 알림톡 테스트입니다.</textarea></td>
</tr>
<tr>
<th align="left">알림톡 제목 (선택)</th>
<td><input type="text" class="list_inputType1 title" size="60" value="테스트 제목" /></td>
</tr>
<tr>
<th align="left">대체문자 본문 (선택)</th>
<td><input type="text" class="list_inputType1 altSms" size="80" value=""></td>
</tr>
</tbody>
</table>
<div style="margin-top:6px;">
<button type="button" class="mem_btn3 removeReceiver">삭제</button>
</div>
</fieldset>
</div>
<div style="margin-top:16px;">
<button type="button" class="mem_btn3" style="width:100%;" id="ajax_send_at" onclick="return false;">알림톡 발송</button>
</div>
</fieldset>
<!-- 결과 -->
<fieldset style="margin-top:16px;">
<legend>발송 결과</legend>
<table border="1" cellspacing="0" cellpadding="6" style="width:100%;">
<colgroup><col style="width:180px"><col></colgroup>
<tbody>
<tr><th align="left">메세지그룹ID</th><td><span id="msgGroupId"></span></td></tr>
<tr><th align="left">결과코드</th><td><span id="resultCode"></span></td></tr>
<tr><th align="left">결과메시지</th><td><span id="resultMsg"></span></td></tr>
<tr><th align="left">성공수량</th><td><span id="successCnt"></span></td></tr>
<tr><th align="left">실패수량</th><td><span id="failCnt"></span></td></tr>
</tbody>
</table>
</fieldset>
</div>
</div>
</div>
</body>
</html>

View File

@ -0,0 +1,108 @@
<%@page import="java.io.InputStreamReader"%>
<%@page import="java.io.BufferedReader"%>
<%@page import="org.apache.http.client.entity.UrlEncodedFormEntity"%>
<%@page import="org.apache.http.client.methods.CloseableHttpResponse"%>
<%@page import="org.apache.http.client.methods.HttpPost"%>
<%@page import="org.apache.http.impl.client.CloseableHttpClient"%>
<%@page import="org.apache.http.impl.client.HttpClients"%>
<%@page import="org.apache.http.message.BasicNameValuePair"%>
<%@page import="org.apache.http.NameValuePair"%>
<%@page import="java.nio.charset.StandardCharsets"%>
<%@page import="java.util.*"%>
<%@ page language="java" contentType="application/json; charset=UTF-8" pageEncoding="UTF-8"%>
<%
response.setCharacterEncoding("UTF-8");
try {
// ===== 고정값(실서비스는 서버 보관) =====
final String MBER_ID = "dudgusw";
final String API_KEY = "3429312e6a2c732188d4cc7d15d8a1baa01d8d91";
// ===== 전송 URL =====
// final String apiUrl = "http://119.193.215.98:8087/api/kakao/at/sendMsg";
final String apiUrl = "http://localhost:8088/api/kakao/at/sendMsg";
// ===== 공통 파라미터 수신 (p_* / 비접두어 둘 다 허용) =====
String senderKey = request.getParameter("p_senderKey"); if (senderKey == null) senderKey = request.getParameter("senderKey");
String templateCode = request.getParameter("p_templateCode"); if (templateCode == null) templateCode = request.getParameter("templateCode");
String callFrom = request.getParameter("p_callFrom"); if (callFrom == null) callFrom = request.getParameter("callFrom");
String subMsgSendYn = request.getParameter("p_subMsgSendYn"); if (subMsgSendYn == null) subMsgSendYn = request.getParameter("subMsgSendYn");
String testYn = request.getParameter("p_testYn"); if (testYn == null) testYn = request.getParameter("testYn");
if (callFrom != null) callFrom = callFrom.replaceAll("\\D", ""); // 숫자만
// ===== form 구성 (x-www-form-urlencoded) =====
List<NameValuePair> form = new ArrayList<>();
form.add(new BasicNameValuePair("mberId", MBER_ID));
form.add(new BasicNameValuePair("accessKey", API_KEY));
form.add(new BasicNameValuePair("senderKey", senderKey != null ? senderKey : ""));
form.add(new BasicNameValuePair("templateCode", templateCode != null ? templateCode : ""));
form.add(new BasicNameValuePair("callFrom", callFrom != null ? callFrom : ""));
form.add(new BasicNameValuePair("subMsgSendYn", subMsgSendYn != null ? subMsgSendYn : ""));
if (testYn != null && !testYn.isEmpty()) {
form.add(new BasicNameValuePair("testYn", testYn)); // snake_case 아님
}
// ===== 수신자 인덱스 수집 (callTo_* / p_callTo_* 모두 지원) =====
Set<Integer> idxSet = new TreeSet<Integer>(); // 자동 오름차순
for (Enumeration<String> e = request.getParameterNames(); e.hasMoreElements();) {
String nm = e.nextElement();
if (nm.startsWith("callTo_")) {
try { idxSet.add(Integer.parseInt(nm.substring("callTo_".length()))); } catch(Exception ignore){}
} else if (nm.startsWith("p_callTo_")) {
try { idxSet.add(Integer.parseInt(nm.substring("p_callTo_".length()))); } catch(Exception ignore){}
}
}
// ===== 인덱스별로 Postman 스타일 키 생성해 그대로 추가 =====
for (Integer idx : idxSet) {
String callTo = request.getParameter("callTo_" + idx);
if (callTo == null) callTo = request.getParameter("p_callTo_" + idx);
if (callTo == null || callTo.trim().isEmpty()) continue;
callTo = callTo.replaceAll("\\D", "");
String title = request.getParameter("templateTitle_" + idx);
if (title == null) title = request.getParameter("p_title_" + idx);
String content = request.getParameter("templateContent_" + idx);
if (content == null) content = request.getParameter("p_msgTxt_" + idx);
String subTxt = request.getParameter("subMsgTxt_" + idx);
if (subTxt == null) subTxt = request.getParameter("p_subMsgTxt_" + idx);
form.add(new BasicNameValuePair("callTo_" + idx, callTo));
if (title != null && !title.isEmpty()) form.add(new BasicNameValuePair("templateTitle_" + idx, title));
if (content != null && !content.isEmpty()) form.add(new BasicNameValuePair("templateContent_" + idx, content));
if (subTxt != null && !subTxt.isEmpty()) form.add(new BasicNameValuePair("subMsgTxt_" + idx, subTxt));
}
// (선택) 서버 로그로 확인
System.out.println("form :: " + form);
// ===== 전송 =====
String result = "";
try (CloseableHttpClient client = HttpClients.createDefault()) {
HttpPost post = new HttpPost(apiUrl);
post.setHeader("Accept", "application/json");
post.setEntity(new UrlEncodedFormEntity(form, StandardCharsets.UTF_8));
try (CloseableHttpResponse res = client.execute(post)) {
if (res != null && res.getEntity() != null) {
try (BufferedReader in = new BufferedReader(
new InputStreamReader(res.getEntity().getContent(), StandardCharsets.UTF_8))) {
String line;
while ((line = in.readLine()) != null) result += line;
}
}
}
}
System.out.println(" ++ result :: "+ result);
out.print((result == null || result.isEmpty())
? "{\"data\":{\"resultCode\":99,\"msg\":\"EMPTY RESPONSE\"}}"
: result);
} catch (Exception e) {
out.print("{\"data\":{\"resultCode\":99,\"msg\":\"WRONG API METHOD\"}}");
e.printStackTrace();
}
%>

View File

@ -0,0 +1,107 @@
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html lang="ko">
<head>
<meta charset="UTF-8">
<title>친구톡(FT) 발송 샘플</title>
<script src="./jquery-3.5.0.js"></script>
</head>
<body>
<h2>친구톡(FT) 발송(샘플)</h2>
<form id="ftForm" method="post" action="./jsp_example_send_ft_r1.jsp" enctype="multipart/form-data">
<fieldset>
<legend>공통</legend>
<table border="1" cellspacing="0" cellpadding="6" style="width:100%;">
<colgroup><col style="width:220px"><col></colgroup>
<tr><th align="left">*senderKey</th><td><input type="text" name="senderKey" size="60" value=""/></td></tr>
<tr><th align="left">adFlag</th><td><input type="text" name="adFlag" size="4" value="N"/> (Y/N)</td></tr>
<tr><th align="left">subMsgSendYn</th><td><input type="text" name="subMsgSendYn" size="4" value="Y"/> (Y/N)</td></tr>
<tr><th align="left">callFrom</th><td><input type="text" name="callFrom" size="20" value="010-9341-4986"/></td></tr>
<tr><th align="left">button(JSON)</th><td>
<textarea name="button" rows="4" cols="80">{ "button": [ { "name":"사이트", "linkType":"WL", "linkMo":"https://m.example.com", "linkPc":"https://www.example.com" } ] }</textarea>
<div style="color:#666;">※ WL/AL만 가능</div>
</td></tr>
<tr><th align="left">templateImage</th><td><input type="file" name="templateImage" accept="image/*"/></td></tr>
<!-- 서버 경로로 보낼 때는 아래 사용(선택) -->
<!-- <tr><th align="left">templateImagePath</th><td><input type="text" name="templateImagePath" size="80" value="/path/to/image.jpg"/></td></tr> -->
<tr><th align="left">testYn</th><td><input type="text" name="testYn" size="6" value=""/> (YS-성공, YF-실패, 공란-실발송)</td></tr>
</table>
</fieldset>
<fieldset style="margin-top:12px;">
<legend>수신자 목록</legend>
<div id="receiverList">
<fieldset class="rItem" style="margin:8px 0;">
<legend>수신자 1</legend>
<table border="1" cellspacing="0" cellpadding="6" style="width:100%;">
<colgroup><col style="width:180px"><col></colgroup>
<tr><th align="left">callTo_1</th><td><input type="text" name="callTo_1" size="30" value="01083584250"/></td></tr>
<tr><th align="left">templateContent_1</th><td><textarea name="templateContent_1" rows="3" cols="80">친구톡 본문 1</textarea></td></tr>
<tr><th align="left">subMsgTxt_1</th><td><input type="text" name="subMsgTxt_1" size="80" value="대체문자 발송1"/></td></tr>
</table>
<div style="margin-top:6px;"><button type="button" class="removeBtn">삭제</button></div>
</fieldset>
</div>
<div style="margin:8px 0;">
<button type="button" id="addReceiver">수신자 추가</button>
</div>
</fieldset>
<div style="margin-top:12px;">
<button type="submit" id="sendBtn" style="width:100%;">친구톡 발송</button>
</div>
</form>
<hr/>
<h3>결과</h3>
<pre id="resultBox" style="white-space:pre-wrap;"></pre>
<script>
$(function(){
// 수신자 추가
$("#addReceiver").on("click", function(){
var n = $(".rItem").length + 1;
var tpl = ''
+ '<fieldset class="rItem" style="margin:8px 0;">'
+ ' <legend>수신자 '+ n +'</legend>'
+ ' <table border="1" cellspacing="0" cellpadding="6" style="width:100%;">'
+ ' <colgroup><col style="width:180px"><col></colgroup>'
+ ' <tr><th align="left">callTo_'+n+'</th><td><input type="text" name="callTo_'+n+'" size="30" value=""/></td></tr>'
+ ' <tr><th align="left">templateContent_'+n+'</th><td><textarea name="templateContent_'+n+'" rows="3" cols="80"></textarea></td></tr>'
+ ' <tr><th align="left">subMsgTxt_'+n+'</th><td><input type="text" name="subMsgTxt_'+n+'" size="80" value=""/></td></tr>'
+ ' </table>'
+ ' <div style="margin-top:6px;"><button type="button" class="removeBtn">삭제</button></div>'
+ '</fieldset>';
$("#receiverList").append(tpl);
});
// 수신자 삭제
$(document).on("click", ".removeBtn", function(){
$(this).closest(".rItem").remove();
$(".rItem").each(function(i){ $(this).find("legend").text("수신자 " + (i+1)); });
});
// 폼 제출 AJAX (파일 포함)
$("#ftForm").on("submit", function(e){
e.preventDefault();
var fd = new FormData(this);
$.ajax({
url: $(this).attr("action"),
type: "POST",
data: fd,
processData: false,
contentType: false,
success: function(ret){
try { $("#resultBox").text(JSON.stringify(ret, null, 2)); }
catch(e) { $("#resultBox").text(ret); }
},
error: function(req, st, err){
$("#resultBox").text("ERROR: " + st + " / " + err);
}
});
});
});
</script>
</body>
</html>

View File

@ -0,0 +1,189 @@
<%@ page language="java" contentType="application/json; charset=UTF-8" pageEncoding="UTF-8"%>
<%@page import="java.io.*"%>
<%@page import="java.util.*"%>
<%@page import="java.nio.charset.StandardCharsets"%>
<%@page import="org.apache.commons.fileupload.FileItem"%>
<%@page import="org.apache.commons.fileupload.disk.DiskFileItemFactory"%>
<%@page import="org.apache.commons.fileupload.servlet.ServletFileUpload"%>
<%@page import="org.apache.http.HttpEntity"%>
<%@page import="org.apache.http.client.methods.CloseableHttpResponse"%>
<%@page import="org.apache.http.client.methods.HttpPost"%>
<%@page import="org.apache.http.entity.ContentType"%>
<%@page import="org.apache.http.entity.mime.MultipartEntityBuilder"%>
<%@page import="org.apache.http.entity.mime.HttpMultipartMode"%>
<%@page import="org.apache.http.impl.client.CloseableHttpClient"%>
<%@page import="org.apache.http.impl.client.HttpClients"%>
<%
response.setCharacterEncoding("UTF-8");
try {
// ===== 고정값(운영에선 서버 보관) =====
final String MBER_ID = "dudgusw";
final String API_KEY = "3429312e6a2c732188d4cc7d15d8a1baa01d8d91";
// final String apiUrl = "http://119.193.215.98:8087/api/kakao/ft/sendMsg";
final String apiUrl = "http://localhost:8088/api/kakao/ft/sendMsg";
// ===== multipart 파싱 (텍스트/파일 모두 수집) =====
Map<String,String> form = new LinkedHashMap<>();
FileItem imageItem = null; // templateImage 업로드 파일
boolean isMp = ServletFileUpload.isMultipartContent(request);
if (isMp) {
DiskFileItemFactory factory = new DiskFileItemFactory();
// (선택) 임시 디스크 디렉터리, 메모리 임계값 등 설정 가능
ServletFileUpload upload = new ServletFileUpload(factory);
upload.setHeaderEncoding("UTF-8");
List<FileItem> items = upload.parseRequest(request);
for (FileItem it : items) {
if (it.isFormField()) {
String name = it.getFieldName();
String value = it.getString("UTF-8");
form.put(name, value);
} else {
if ("templateImage".equals(it.getFieldName()) && it.getSize() > 0) {
imageItem = it;
}
}
}
} else {
// x-www-form-urlencoded 등 fallback
for (Enumeration<String> e = request.getParameterNames(); e.hasMoreElements();) {
String k = e.nextElement();
form.put(k, request.getParameter(k));
}
}
// ===== 공통 파라미터 추출 (p_* / 비접두어 둘 다 허용) =====
String senderKey = form.get("p_senderKey"); if (senderKey == null) senderKey = form.get("senderKey");
String adFlag = form.get("p_adFlag"); if (adFlag == null) adFlag = form.get("adFlag");
String subMsgSendYn = form.get("p_subMsgSendYn"); if (subMsgSendYn == null) subMsgSendYn = form.get("subMsgSendYn");
String callFrom = form.get("p_callFrom"); if (callFrom == null) callFrom = form.get("callFrom");
String buttonJson = form.get("p_button"); if (buttonJson == null) buttonJson = form.get("button");
String testYn = form.get("p_testYn"); if (testYn == null) testYn = form.get("testYn");
if (callFrom != null) callFrom = callFrom.replaceAll("\\D", "");
// ===== 수신자 인덱스 수집 (callTo_* / p_callTo_*) =====
Set<Integer> idxSet = new TreeSet<>();
for (String k : form.keySet()) {
if (k.startsWith("callTo_")) {
try { idxSet.add(Integer.parseInt(k.substring("callTo_".length()))); } catch(Exception ignore){}
} else if (k.startsWith("p_callTo_")) {
try { idxSet.add(Integer.parseInt(k.substring("p_callTo_".length()))); } catch(Exception ignore){}
}
}
// ===== 전송 직전 로그 (요청 최종 데이터 그대로) =====
System.out.println("===== [FT SEND] FINAL FIELDS =====");
System.out.println("mberId = " + MBER_ID);
System.out.println("accessKey = " + API_KEY); // 운영에선 마스킹 권장
System.out.println("senderKey = " + senderKey);
System.out.println("adFlag = " + adFlag);
System.out.println("subMsgSendYn = " + subMsgSendYn);
System.out.println("callFrom = " + callFrom);
System.out.println("button = " + buttonJson);
System.out.println("testYn = " + testYn);
for (Integer i : idxSet) {
String callTo = form.get("callTo_" + i); if (callTo == null) callTo = form.get("p_callTo_" + i);
String content = form.get("templateContent_" + i); if (content == null) content = form.get("p_templateContent_" + i);
String subTxt = form.get("subMsgTxt_" + i); if (subTxt == null) subTxt = form.get("p_subMsgTxt_" + i);
System.out.println("--- Receiver #" + i + " ---");
System.out.println("callTo_" + i + " = " + callTo);
System.out.println("templateContent_" + i + " = " + content);
System.out.println("subMsgTxt_" + i + " = " + subTxt);
}
String imageLog = "none";
if (imageItem != null) {
imageLog = "[upload] name=" + imageItem.getName() + ", size=" + imageItem.getSize();
} else {
String imagePath = form.get("templateImagePath");
if (imagePath != null && !imagePath.isEmpty()) {
File f = new File(imagePath);
if (f.exists() && f.isFile()) {
imageLog = "[path] " + f.getAbsolutePath() + " (size=" + f.length() + ")";
}
}
}
System.out.println("templateImage = " + imageLog);
System.out.println("===================================");
// ===== 원 API로 멀티파트 전송 =====
MultipartEntityBuilder mb = MultipartEntityBuilder.create()
.setMode(HttpMultipartMode.BROWSER_COMPATIBLE)
.setCharset(StandardCharsets.UTF_8);
ContentType TEXT = ContentType.TEXT_PLAIN.withCharset(StandardCharsets.UTF_8);
// 공통
mb.addTextBody("mberId", MBER_ID, TEXT);
mb.addTextBody("accessKey", API_KEY, TEXT);
if (senderKey != null) mb.addTextBody("senderKey", senderKey, TEXT);
if (adFlag != null) mb.addTextBody("adFlag", adFlag, TEXT);
if (subMsgSendYn != null) mb.addTextBody("subMsgSendYn", subMsgSendYn, TEXT);
if (callFrom != null) mb.addTextBody("callFrom", callFrom, TEXT);
if (buttonJson != null && !buttonJson.isEmpty()) mb.addTextBody("button", buttonJson, TEXT);
if (testYn != null && !testYn.isEmpty()) mb.addTextBody("testYn", testYn, TEXT);
// 수신자
for (Integer i : idxSet) {
String callTo = form.get("callTo_" + i); if (callTo == null) callTo = form.get("p_callTo_" + i);
String content = form.get("templateContent_" + i); if (content == null) content = form.get("p_templateContent_" + i);
String subTxt = form.get("subMsgTxt_" + i); if (subTxt == null) subTxt = form.get("p_subMsgTxt_" + i);
if (callTo == null || callTo.trim().isEmpty()) continue;
callTo = callTo.replaceAll("\\D","");
mb.addTextBody("callTo_" + i, callTo, TEXT);
if (content != null && !content.isEmpty()) mb.addTextBody("templateContent_" + i, content, TEXT);
if (subTxt != null && !subTxt.isEmpty()) mb.addTextBody("subMsgTxt_" + i, subTxt, TEXT);
}
// 이미지
if (imageItem != null) {
mb.addBinaryBody("templateImage", imageItem.getInputStream(),
ContentType.DEFAULT_BINARY, imageItem.getName());
} else {
String imagePath = form.get("templateImagePath");
if (imagePath != null && !imagePath.isEmpty()) {
File f = new File(imagePath);
if (f.exists() && f.isFile()) {
mb.addBinaryBody("templateImage", f, ContentType.DEFAULT_BINARY, f.getName());
}
}
}
HttpEntity entity = mb.build();
String result = "";
try (CloseableHttpClient client = HttpClients.createDefault()) {
HttpPost post = new HttpPost(apiUrl);
post.setHeader("Accept", "application/json");
post.setEntity(entity);
try (CloseableHttpResponse res = client.execute(post)) {
if (res != null && res.getEntity() != null) {
try (BufferedReader br = new BufferedReader(
new InputStreamReader(res.getEntity().getContent(), StandardCharsets.UTF_8))) {
String line;
while ((line = br.readLine()) != null) result += line;
}
}
}
}
out.print((result == null || result.isEmpty())
? "{\"data\":{\"resultCode\":99,\"msg\":\"EMPTY RESPONSE\"}}"
: result);
} catch (Exception e) {
out.print("{\"data\":{\"resultCode\":99,\"msg\":\"WRONG API METHOD\"}}");
e.printStackTrace();
}
%>

View File

@ -15,6 +15,14 @@
<h2><a href="./jsp_example_hstry_form_r1.jsp">3.전체발송내역</a></h2>
<h2><a href="./jsp_example_hstry_detail_form_r1.jsp">4.상세발송내역</a></h2>
<h2><a href="./jsp_example_select_price_form_r1.jsp">5.발송가능건수</a></h2>
<hr style="margin: 20px 0;">
<!-- 카카오톡 메시지 메뉴 -->
<h2><a href="./jsp_example_send_at_form_r1.jsp">6.알림톡 발송</a></h2>
<h2><a href="./jsp_example_send_ft_form_r1.jsp">7.친구톡 발송</a></h2>
<hr style="margin: 20px 0;">
<!-- 조회 메뉴 -->
<h2><a href="./jsp_example_inqry_chnlid_form_r1.jsp">8.알림톡 채널ID 조회</a></h2>
<h2><a href="./jsp_example_inqry_templates_list_form_r1.jsp">9.알림톡 템플릿 목록 조회</a></h2>
</div>
</div>