Spring
부트캠프88일차(ajax 2)
동곤일상
2025. 6. 17. 17:24
반응형
1) text파일과 ajax활용 select바 활성화
2) 웹크롤링 (ajax활용)
2-1) html(문자열)전송방식
2-2) Map객체(JSON방식) 더 많이사용
3) 작성자 별 게시판 등록갯수 조회
1) text파일과 ajax활용 select바 활성화
sido.txt
0.70MB
layout쪽에 해당 스크립트를 추가해주자
$(function(){
getSido();
})
function getSido(){
$.ajax({
url:"/ajax/select1",
success:function(data){
console.log(data)
let arr=data.substring
(data.indexOf('[')+1, data.indexOf(']')).split(",");
$.each(arr,function(i,item){
$("select[name=si]").append(function(){
return "<option>"+item+"</option>"
})
})
}
})
}
스크립트에서 사용한 substring문의 이해를 돕기위한 코드
ajaxController의 일부분
package kr.gdu.controller;
@RestController
@RequestMapping("ajax")
public class AjaxController {
@Autowired
BoardService service;
//produces="text/plain; charset=UTF-8" : 전송될 데이터의 형식
@RequestMapping(value="select1",produces = "text/plain; charset=UTF-8")
public String sidoSelect1(String si,String gu) {
return service.sidoSelect1(si,gu);
}
}
BoardService의 sidoSelect1메서드 부분
public String sidoSelect1(String si, String gu) {
BufferedReader fr = null;
String path = UPLOAD_IMAGE_DIR+"data/sido.txt";
try {
//sido.txt파일을 읽어 fr에저장
fr = new BufferedReader(new FileReader(path));
} catch (Exception e) {
e.printStackTrace();
}
Set<String> set = new LinkedHashSet<>();
String data = null;
if(si==null&&gu==null) {
try {
while((data=fr.readLine())!=null) {
// '\\s+' : 1개이상의 공백
String[] arr = data.split("\\s+");
if(arr.length>=3) {
//시군구가 존재할 때 시만 set에 넣는다(중복방지)
set.add(arr[0].trim());
}
}
}
catch (IOException e) {
e.printStackTrace();
}
}
List<String> list = new ArrayList<>(set);
return list.toString();//[서울특별시,경기도,강원도,,..,,]
}
컨트롤러와서비스 구현을 마쳤다면
다음과같이 console을 확인가능함
아까 [ ,] 를제외하고 , 를 기준으로 배열을 만들었음
1-2) 이제 시를 선택하면 그 시에 해당하는 구 동을 뽑아보자
layout.jsp의 일부분
<footer class="footer text-muted">
© 2025 MyAdmin. All <span id="si"> <select name="si"
onchange="getText('si')">
<option value="">시도를 선택하세요</option>
</select>
</span> <span id="gu"> <select name="gu" onchange="getText('gu')">
<option value="">구군을 선택하세요</option>
</select>
</span> <span id="dong"> <select name="dong">
<option value="">동리를 선택하세요</option>
</select>
</span>
</footer>
<script>
function getText(name){
let city = $("select[name='si']").val();
let gu = $("select[name='gu']").val();
let disname;
//최상단에 뜰 text
let toptext="구군을 선택하세요";
let params="";
if(name=="si"){
params = "si="+city.trim();
disname="gu";
}
else if(name="gu"){
params="si="+city.trim()+"&gu="+gu.trim();
disname="dong";
toptext="동리를 선택하세요";
}
else{
return;
}
$.ajax({
url:"/ajax/select2",
type:"POST",
data:params,
success:function(arr){//서버에서 배열객체로전달받음
console.log(arr);
$("select[name="+disname+"] option").remove();
$("select[name="+disname+"]").append(function(){
return "<option value=''>"+toptext+"</option>"
})
$.each(arr,function(i,item){
$("select[name="+disname+"]").append(function(){
return "<option>"+item+"</option>"
})
})
}
})
}
</script>
요청url이 ajax로 매핑이되니깐 그에 해당하는 ajaxController로 가야겠죠?
ajaxController의 일부분
/ajax/select2의 요청을 받을 수 있게끔 value를 설정
(List를 반환하므로 produces는 설정X)
@RestController
@RequestMapping("ajax")
public class AjaxController {
@Autowired
BoardService service;
@RequestMapping(value="select2")
public List<String> sigunSelect2(String si ,String gu) {
return service.sigunSelect2(si,gu); //list객체를 클라이언트로 전달
}
}
Service의 일부
public List<String> sigunSelect2(String si, String gu) {
BufferedReader fr = null;
String path = UPLOAD_IMAGE_DIR+"data/sido.txt";
try {
//sido.txt파일을 읽어 fr에저장
fr = new BufferedReader(new FileReader(path));
} catch (Exception e) {
e.printStackTrace();
}
Set<String> set = new LinkedHashSet<>();
String data = null;
if(si==null&&gu==null) {
try {
while((data=fr.readLine())!=null) {
// '\\s+' : 1개이상의 공백
String[] arr = data.split("\\s+");
if(arr.length>=3) {
//시군구가 존재할 때 시만 set에 넣는다(중복방지)
set.add(arr[0].trim());
}
}
}
catch (IOException e) {
e.printStackTrace();
}
}
else if(gu==null) {
si = si.trim();
try {
while((data=fr.readLine())!=null) {
String[] arr = data.split("\\s+");
// 0번쨰요소가 si인
if(arr.length>=3 && arr[0].equals(si)
&& !arr[1].contains(arr[0])) {
set.add(arr[1].trim());
}
}
}
catch(IOException e) {
e.printStackTrace();
}
}
else {
si = si.trim();
gu=gu.trim();
try {
while((data=fr.readLine())!=null) {
String[] arr = data.split("\\s+");
//0번쨰요소가si , 1번쨰요소가gu (중복금지)
if(arr.length>=3 && arr[0].equals(si)
&& arr[1].equals(gu) && !arr[0].equals(arr[1])
&& !arr[2].contains(arr[1])) {
if(arr.length>3) {
/*배열크기가 3초과 일 경우에는
arr[3]를 arr[2]에 합침
*/
if(arr[3].contains(arr[1])) {
continue;
}
arr[2] += " "+arr[3];
}
set.add(arr[2].trim());
}
}
}
catch(IOException e) {
e.printStackTrace();
}
}
ArrayList<String> list = new ArrayList<>(set);
System.out.println("list ::::: "+list);
return list;
}
2) 웹크롤링 (ajax활용)
두가지의 방식으로 진행해 볼 예정
2-1) html(문자열)전송방식
layout.jsp의 일부분
$(function(){
exchangeRate();
})
function exchangeRate(){
$.ajax("/ajax/exchange1",{
success: function(data){
console.log(data);
$("#exchange").html(data)
},
error:function(e){
alert("환율조회시 서버오류발생",e.status)
}
})
}
ajaxcontroller
@RequestMapping(value="exchange1",produces = "text/plain; charset=UTF-8")
public String exchange() {
//미국달러,중국,일본,유로 4개통화만
return service.exchange1();
}
service
(url : 한국수출입은행사이트url)
public String exchange1() {
Document doc = null;
List<List<String>> trlist = new ArrayList<>();
String url = "https://www.koreaexim.go.kr/wg/HPHKWG057M01";
String exdate = null;
try {
doc = Jsoup.connect(url).get();
Elements trs = doc.select("tr"); //tr태그를 다 가져와
exdate = doc.select("p.table-unit").html();
for(Element tr : trs) {
List<String> tdlist = new ArrayList<>();
Elements tds = tr.select("td");//tr태그내의 td를 모두가져와
for(Element td : tds) {
tdlist.add(td.html());//td태그내의 내용들 (innerHTML이라생각하면댐)
//[USD,미국달러,1325,..]
}
if(tdlist.size()>0) {
if(tdlist.get(0).equals("USD")|| tdlist.get(0).equals("CNH")
|| tdlist.get(0).equals("JPY(100)")|| tdlist.get(0).equals("EUR")) {
trlist.add(tdlist);
}
}
}
}
catch (Exception e) {
e.printStackTrace();
}
StringBuilder sb = new StringBuilder();
sb.append("<h4 class='w3-center'>수출입은행<br>"+exdate+"</h4>");
sb.append("<table class='w3-table-all'>");
sb.append("<tr><th>통화</th><th>기준율</th><th>받으실 때</th><th>보내실 때</th></tr>");
for (List<String> tds : trlist) {
sb.append("<tr><td>"+tds.get(0)+"<br>"+tds.get(1)+"</td><td>"+tds.get(4)+"</td>");
sb.append("<td>"+tds.get(2)+"</td><td>"+tds.get(3)+"</td><tr>");
}
sb.append("</table>");
return sb.toString();
}
2-2) Map객체(JSON방식) 더 많이사용
layout.jsp의 script부분
function exchangeRate2(){ //json방식
$.ajax("/ajax/exchange2",{
success: function(json){ //서버Map객체로 전송 , 클라이언트 : JSON형식
console.log(json);
let html = "<h4 class='text-center my-3'>수출입은행<br>"+json.exdate +"</h4>";
html += "<table class='table table-bordered table-hover text-center'>";
html += "<thead><tr><th>통화</th><th>기준율</th><th>받으실때</th>"
+"<th>보내실때</th></tr></thead>";
$.each(json.trlist,function(i,tds){
html += "<tr><td>"+tds[0]+"<br>"+tds[1]+"</td><td>"+tds[4]+"</td>"
+"<td>"+tds[2]+"</td><td>"+tds[3]+"</td></tr>"
})
html += "</table>"
$("#exchange").html(html);
},
error:function(e){
alert("환율조회시 서버오류발생",e.status)
}
})
}
Ajaxcontroller
@RequestMapping(value="exchange2")
public Map<String, Object> exchange2() { //json 데이터로 전송
return service.exchange2();
}
Service
//JSON
public Map<String, Object> exchange2() {
Document doc = null;
List<List<String>> trlist = new ArrayList<>();
String url = "https://www.koreaexim.go.kr/wg/HPHKWG057M01";
String exdate = null;
try {
doc = Jsoup.connect(url).get();
Elements trs = doc.select("tr"); //tr태그를 다 가져와
//exdate = p태그의 class = table-unit
exdate = doc.select("p.table-unit").html();
for(Element tr : trs) {
List<String> tdlist = new ArrayList<>();
Elements tds = tr.select("td");//tr태그내의 td를 모두가져와
for(Element td : tds) {
tdlist.add(td.html());//td태그내의 내용들 (innerHTML이라생각하면댐)
//[USD,미국달러,1325,..]
}
if(tdlist.size()>0) {
if(tdlist.get(0).equals("USD")|| tdlist.get(0).equals("CNH")
|| tdlist.get(0).equals("JPY(100)")|| tdlist.get(0).equals("EUR")) {
trlist.add(tdlist);
}
}
}
}
catch (Exception e) {
e.printStackTrace();
}
Map<String, Object> map = new HashMap<>();
//map에 담기
map.put("exdate", exdate);
map.put("trlist", trlist);
return map;
}
3) 작성자 별 게시판 등록갯수 조회
layout의 body부분
<div id="mainContent" class="main-content">
<div class="radio-toggle">
<div class="btn-group" role="group" aria-label="Pie chart selection">
<label class="btn btn-outline-primary active"> <input
type="radio" name="pie" onchange="piegraph(2)" checked>
자유게시판
</label> <label class="btn btn-outline-primary"> <input type="radio"
name="pie" onchange="piegraph(3)"> Q&A
</label>
</div>
<div id="piecontainer" style="width: 100%; height: 250px;"></div>
</div>
<sitemesh:write property="body" />
</div>
자유게시판 , Q&A버튼을 만들어
클릭 시 각각 다른함수가 호출되게끔 작성함
<script
src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.8.0/Chart.bundle.min.js"></script>
<script
src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.8.0/Chart.min.js"></script>
$(function(){
getSido1();
//exchangeRate(); //수출입은행 환율정보조회 서버에서 HTML형식(문자열)으로 리턴
exchangeRate2(); //서버에서 Map형식(JSON)으로 리턴
piegraph(2) //글쓴이별 게시글 건수를 파이그래프로출력
boardImg();
})
let randomColorFactor = function(){
return Math.round(Math.random()*255)
}
let randomColor = function(opa){
return "rgba("+randomColorFactor()+","
+randomColorFactor()+","
+randomColorFactor()+","
+(opa || ".3") + ")";
} //각각의 색깔
function piegraph(id){
$.ajax("/ajax/graph1?id="+id,{
success : function(json){
// 캔버스 생성 시 높이 속성 추가
let canvas = "<canvas id='canvas1' style='width:100%; height:100px;'></canvas>"; // 캔버스 높이 직접 지정 (예: 100px)
$("#piecontainer").html(canvas);
//canvas에 그래프를 넣을것임
pieGraphPrint(json,id);
},
error : function(e){
alert("서버오류 : "+e.status);
}
})
}
function pieGraphPrint(arr,id){
let colors = [];
let writers = [];
let datas = [];
$.each(arr,function(index){
colors[index] = randomColor(0.5);
for(key in arr[index]){
writers.push(key);
datas.push(arr[index][key]);
}
})
let title = (id == 2)?"자유게시판":"Q&A";
let config = {
type:'pie',
data : {
datasets : [{ data:datas,
backgroundColor : colors}],
labels : writers
},
options : {
responsive : true,
maintainAspectRatio: false, // 가로-세로 비율 유지를 비활성화합니다.
legend : {
display:true,
position:"right",
labels: {
boxWidth: 15, // 범례 상자의 너비를 더 줄입니다.
padding: 5 // 범례 항목 간의 간격을 더 줄입니다.
}
},
title : {
display : true,
text : '글쓴이 별 '+title+"등록건수",
position : 'bottom',
fontSize: 10 // 제목 글씨 크기를 더 줄입니다.
},
layout: {
padding: {
left: 5, // 그래프 내부 여백을 더 줄입니다.
right: 5,
top: 5,
bottom: 5
}
},
elements: {
arc: {
borderWidth: 0 // 조각의 테두리를 제거하여 시각적으로 더 작게 보이게 할 수 있습니다.
}
}
}
}
let ctx = document.getElementById("canvas1")
new Chart(ctx,config)
}
AjaxController
@RequestMapping(value="graph1")
public List<Map.Entry<String, Integer>> graph1(String id) {
Map<String,Integer> map = service.graph1(id);
List<Map.Entry<String, Integer>> list = new ArrayList<>();
for (Entry<String, Integer> m : map.entrySet()) {
list.add(m);
}
//value 크기별 정렬(내림차순)!!
Collections.sort(list,(m1,m2)->m2.getValue() - m1.getValue());
return list;
}
service
(dao와mapper는 생략하겠음 where boardid=#{boardid}로 설정 후
상위7개컬럼의 작성자,작성게시물갯수(count(*))을 뽑는 내용)
public Map<String, Integer> graph1(String id) {
List<Map<String,Object>> list = boardDao.graph1(id);
Map<String,Integer> map = new HashMap<>();
for (Map<String, Object> m : list) {
String writer = (String)m.get("writer");
long cnt = (Long)m.get("cnt");
map.put(writer, (int)cnt);
}
return map;
}