예시 코드에서는 URLEncoder.encode()
를 쓴다. ASCII가 아닌 한글같은 게 있을 수도 있으니까 쓰는거
[Java] String이 불변 객체인 이유는 무엇일까?
Java String이 불변(immutable)인 이유
자바에서 문자열 합칠 때 '+' 연산을 쓰지 마세요! (StringBuilder, StringJoiner, String.join, StringBuffer)
@GetMapping("/airKorea")
public String airKorea(Model model) throws IOException {
StringBuilder urlBuilder = new StringBuilder("http://apis.data.go.kr/B552584/ArpltnInforInqireSvc/getMinuDustFrcstDspth"); // String 쓰면 메모리 왔다갔다 느려질것
urlBuilder.append("?serviceKey="+util.getServiceKey());
urlBuilder.append("&returnType=xml");
urlBuilder.append("&numOfRows=100");
urlBuilder.append("&pageNo=1");
urlBuilder.append("&searchDate=2024-03-11");
urlBuilder.append("&informCode=PM10");
URL url = new URL(urlBuilder.toString());
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
conn.setRequestMethod("GET");
conn.setRequestProperty("Content-type", "application/json");
System.out.println("Response code: " + conn.getResponseCode()); // 접속 결과. 200대가 정상 접속.
BufferedReader rd;
if(conn.getResponseCode() >= 200 && conn.getResponseCode() <= 300) {
rd = new BufferedReader(new InputStreamReader(conn.getInputStream()));
} else { // 비정상 통신
rd = new BufferedReader(new InputStreamReader(conn.getErrorStream()));
}
StringBuilder sb = new StringBuilder();
String line;
while((line = rd.readLine()) != null) { // 몇 줄 있을지 모르니까, rd.readLine 없으면 null로 처리할거
sb.append(line);
}
rd.close();
conn.disconnect();
System.out.println(sb.toString());
//data.go.kr에서 에어코리아 접속, 데이터 불러오기 2개 한 것.
return "airkorea"; //반드시 url과 같을 필요가 없다. 파일명을 넣는다.
}
여기까지 하면 sb.toString()을 출력했을 때 한 줄로 xml string이 온다.
데이터가 왔고,
이제는 parsing한다.
returnType을 json으로 바꿈
json모양
JSON Beautifier(JSON Viewer)
구조를 보도록 하자
xml은 xml viewer
jsonformatter xml-viewer
xml은 html처럼 순서가 있다.
json과 xml의 문서 형식은 꼭 알아둬야 한다.
나중에 크롬 익스텐션 JSON VIEWER를 쓸 수도 있다
dependencies에 추가
implementation 'com.googlecode.json-simple:json-simple:1.1.1'
우리는 이미 프로젝트에 jackson이 있어서 JSON을 쓸 수 있지만
제일 쉬워서 이거의 도움을 받을 것.
나중에는 jackson으로 해도 된다.
import org.json.simple.JSONObject;
아니었다
import org.json.simple.parser.JSONParser;
이거 사용
BufferReader
부분은 다 주석처리
// json -> 자바 데이터 타입 형태로 변경 -> 출력
JSONParser parser = new JSONParser();
JSONObject jsonObject = (JSONObject) parser.parse(new InputStreamReader(url.openStream()));
//System.out.println(jsonObject.toString());
//System.out.println(jsonObject.toJSONString());
Map<String, Object> map = (Map<String, Object>) jsonObject.get("response");
System.out.println(map);
이거의 결과는 response 부분이 나오는 것
이런 식으로 포장된 json을 풀어주는 것.
Map<String, Object> map = (Map<String, Object>) jsonObject.get("response"); // 포장된 json을 풀어주는 것
//System.out.println(map);
map = (Map<String, Object>) map.get("body");
System.out.println(map);
얘도 됐다
Map<String, Object> map = (Map<String, Object>) ((HashMap) jsonObject.get("response")).get("body"); // 포장된 json을 풀어주는 것
System.out.println(map);
결과를 보면 items는 array, list 모양
items list는 JSONArray로 받는다
Map<String, Object> map = (Map<String, Object>) jsonObject.get("response"); // 포장된 json을 풀어주는 것
//System.out.println(map);
map = (Map<String, Object>) map.get("body");
System.out.println(map);
JSONArray jsonArray = (JSONArray) map.get("items");
System.err.println(jsonArray);
나온 모양. 빨간색,, (System.err.println()
으로 출력해버려서)
json은 순서가 없지만 jsonArray는 배열이기 때문에 유지된다고 한다
좀더 복잡하다
Document는 w3c
import org.w3c.dom.Document;
@GetMapping("/airKoreaXML")
public String airKoreaXML(Model model) throws IOException, ParserConfigurationException, SAXException {
StringBuilder urlBuilder = new StringBuilder("http://apis.data.go.kr/B552584/ArpltnInforInqireSvc/getMinuDustFrcstDspth"); // String 쓰면 메모리 왔다갔다 느려질것
urlBuilder.append("?serviceKey="+util.getServiceKey());
urlBuilder.append("&returnType=xml");
urlBuilder.append("&numOfRows=100");
urlBuilder.append("&pageNo=1");
urlBuilder.append("&searchDate=2024-03-13");
urlBuilder.append("&informCode=PM10");
URL url = new URL(urlBuilder.toString());
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
conn.setRequestMethod("GET");
conn.setRequestProperty("Content-type", "application/json");
System.out.println("Response code: " + conn.getResponseCode()); // 응답 결과: 200
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); // 자바는 공장 만들어야
DocumentBuilder documentBuilder = factory.newDocumentBuilder(); // 공장에서 DocumentBuilder 만든 건가... -> 이때 ParserConfigurationException
Document document = documentBuilder.parse(conn.getInputStream()); // 이때 SAXException
document.getDocumentElement().normalize(); // document 정규화
System.out.println(document.getDocumentElement().getNodeName()); // node nodeName == DOM(document object model)
return "airkorea"; //반드시 url과 같을 필요가 없다. 파일명을 넣는다.
}
nodeName response는 이건가보다
response의 자식 요소 body -> items로 가려고 한다
@GetMapping("/airKoreaXML")
public String airKoreaXML(Model model) throws IOException, ParserConfigurationException, SAXException {
StringBuilder urlBuilder = new StringBuilder("http://apis.data.go.kr/B552584/ArpltnInforInqireSvc/getMinuDustFrcstDspth"); // String 쓰면 메모리 왔다갔다 느려질것
urlBuilder.append("?serviceKey="+apiInfo.getServiceKey());
urlBuilder.append("&returnType=xml");
urlBuilder.append("&numOfRows=100");
urlBuilder.append("&pageNo=1");
urlBuilder.append("&searchDate=2024-03-13");
urlBuilder.append("&informCode=PM10");
URL url = new URL(urlBuilder.toString());
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
conn.setRequestMethod("GET");
conn.setRequestProperty("Content-type", "application/json");
System.out.println("Response code: " + conn.getResponseCode()); // 응답 결과: 200
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); // 자바는 공장 만들어야
DocumentBuilder documentBuilder = factory.newDocumentBuilder(); // 공장에서 DocumentBuilder 만든 건가... -> 이때 ParserConfigurationException
Document document = documentBuilder.parse(conn.getInputStream()); // 이때 SAXException
document.getDocumentElement().normalize(); // document 정규화
System.out.println("response : " + document.getDocumentElement().getNodeName()); // node nodeName == DOM(document object model)
NodeList list = (NodeList) document.getDocumentElement().getChildNodes().item(3);//body
System.out.println(list.getLength());
System.out.println(list.item(1).getNodeName());//items
NodeList list2 = list.item(1).getChildNodes();//items == 13개
//System.out.println("items :: " + list2.getLength());
//System.out.println("items :: " + list2.item(0).getNodeName());
for (int i = 0; i < list2.getLength(); i++) {
NodeList list3 = list2.item(i).getChildNodes();//item == 13개
for (int j = 1; j < list3.getLength(); j++) {
Node node = list3.item(j);
if(node.getNodeType() == Node.ELEMENT_NODE) {
System.out.println(j + " : " + list3.item(j).getNodeName() + " : " + list3.item(j).getTextContent());
}
}
}
return "airkoreaXML"; //반드시 url과 같을 필요가 없다. 파일명을 넣는다.
}
데이터 구조를 보도록...
xml은 이렇게 복잡해서 json이 대부분이다.
map에 넣어서 보내주는 걸로..
@GetMapping("/airKoreaXML")
public String airKoreaXML(Model model) throws IOException, ParserConfigurationException, SAXException {
StringBuilder urlBuilder = new StringBuilder("http://apis.data.go.kr/B552584/ArpltnInforInqireSvc/getMinuDustFrcstDspth"); // String 쓰면 메모리 왔다갔다 느려질것
urlBuilder.append("?serviceKey="+apiInfo.getServiceKey());
urlBuilder.append("&returnType=xml");
urlBuilder.append("&numOfRows=100");
urlBuilder.append("&pageNo=1");
urlBuilder.append("&searchDate=2024-03-13");
urlBuilder.append("&informCode=PM10");
URL url = new URL(urlBuilder.toString());
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
conn.setRequestMethod("GET");
conn.setRequestProperty("Content-type", "application/json");
System.out.println("Response code: " + conn.getResponseCode()); // 응답 결과: 200
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); // 자바는 공장 만들어야
DocumentBuilder documentBuilder = factory.newDocumentBuilder(); // 공장에서 DocumentBuilder 만든 건가... -> 이때 ParserConfigurationException
Document document = documentBuilder.parse(conn.getInputStream()); // 이때 SAXException
document.getDocumentElement().normalize(); // document 정규화
System.out.println("response : " + document.getDocumentElement().getNodeName()); // node nodeName == DOM(document object model)
NodeList list = (NodeList) document.getDocumentElement().getChildNodes().item(3);//body
System.out.println(list.getLength());
System.out.println(list.item(1).getNodeName());//items
NodeList list2 = list.item(1).getChildNodes();//items == 13개
//System.out.println("items :: " + list2.getLength());
//System.out.println("items :: " + list2.item(0).getNodeName());
// List 생성 - map 20개 담아서 보내는 리스트
List<Map<String, Object>> mapList = new ArrayList<Map<String,Object>>(); // 20개 담아줄것
for (int i = 1; i < list2.getLength(); i++) { // i = 1로... node는 1로. 0하면 맨앞에 {}가 한 개 생기더라
NodeList list3 = list2.item(i).getChildNodes();//item == 13개
// Map 생성 - 각각의 데이터를 담아주는 맵
Map<String, Object> ele = new HashMap<String, Object>();
for (int j = 1; j < list3.getLength(); j++) {
Node node = list3.item(j);
if(node.getNodeType() == Node.ELEMENT_NODE) {
//System.out.println(j + " : " + list3.item(j).getNodeName() + " : " + list3.item(j).getTextContent());
//담기 - node이름을 key, 글 내용을 value로
ele.put(list3.item(j).getNodeName(), list3.item(j).getTextContent());
//System.out.println("map: " + ele);
}
}
mapList.add(ele);
//System.out.println(mapList);
}
model.addAttribute("data", mapList);
return "airkoreaXML"; //반드시 url과 같을 필요가 없다. 파일명을 넣는다.
}
자바로 만들어진 HTML Parser
dependency 추가
// https://mvnrepository.com/artifact/org.jsoup/jsoup
implementation 'org.jsoup:jsoup:1.17.2'
html 긁어온다
a.menu-list.somoim > .menu_over
에서 .menu_over
까지 안 들어가면 span태그 .까지 같이 나옴.
.menu_over
은 for문 안의 ele.text() 쪽에서 들어가도 된다
@GetMapping("/html")
public String html() throws IOException {
org.jsoup.nodes.Document doc = Jsoup.connect("http://www.naver.com").get(); // 실제 사이트에서 값 가져오게
System.out.println(doc);
return "html";
}
예매 사이트도 html 가져올 수 있다?
클리앙 메뉴 가져오기
@GetMapping("/html")
public String html() throws IOException {
org.jsoup.nodes.Document doc = Jsoup.connect("https://www.clien.net/service/").get(); // 실제 사이트에서 값 가져오게
Elements element = doc.select("a.menu-list.somoim > .menu_over");
System.out.println(element.size()); //몇 개?
for (Element ele : element) {
System.out.println(ele.text()); // .menu_over가 타이틀입니다.
}
//System.out.println(doc);
return "html";
}
jsoup 파이썬할때도 이런 애들 많이 쓴다. parsing할 때.
[JS] 스크롤 프로그레스 바 만들기
js가 높이 나누기 계산해서 넣는 것
[JavaScript] 무한 스크롤 구현하기(Intersection Observer API)
heidiSQL을 켰다
SELECT mm.*
FROM (SELECT CAST(@ROWNUM:=@ROWNUM+1 AS UNSIGNED) AS rowNum, m.*
FROM multiboard m, (SELECT @ROWNUM:=0) AS R
WHERE mtcate=1
ORDER BY mtno ASC) mm
ORDER BY mtno DESC
LIMIT 0, 10
rowNum으로 mtno를 보완
SELECT ff.*, m.mname
FROM(SELECT @ROWNUM:=@ROWNUM+1 AS rowNum, bb.*, (SELECT COUNT(*) FROM multiboard WHERE mtdel=1) AS COUNT
FROM (SELECT b.mtno, b.mttitle, b.mtdate, b.mtdel, b.mno, b.mtcate
FROM multiboard b) bb, (SELECT @ROWNUM:=0) AS R
WHERE mtcate=1
ORDER BY bb.mtno ASC) AS ff JOIN member m ON ff.mno = m.mno
ORDER BY mtno DESC
LIMIT 0, 10
이것은 또 무엇인지 알 수 없었다.. 확인해보기
orm은 쿼리문을 짜지 않는다.
orm을 구현한 게 jpa
자바 최고 난이도
(프로젝트도 db도 새로 만든다고 하셨다)
새 프로젝트 spring starter project
(타임리프는 추가)
Mybatis는 dependency에서 뺀다. 안 씀.
#DB
spring.datasource.driver-class-name=org.mariadb.jdbc.Driver
spring.datasource.url=jdbc:mariadb://db주소:포트/db명
spring.datasource.username=아이디
spring.datasource.password=비번
#port
server.port=80
application.properties에는 이정도만
[Spring JPA ] 데이터베이스 방언(Dialect) 이란?
oracle, mariadb 다 Dialect가 설정돼있다.
우리는 application.properties 쓰는데 application.yml도 있을 수 있다,,?
얘도 application.properties에 추가
#jpa 방언 설정
spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.MariaDB103Dialect
spring.jpa.hibernate.ddl-auto=update
spring.jpa.properties.hibernate.format_sql=true
spring.jpa.properties.hibernate.show_sql=true
create : 실행할 때마다 기존 테이블 삭제 후 다시 생성 (DROP + CREATE)
create-drop : create와 같으나 애플리케이션 종료 시점에 테이블 삭제
update : 엔티티 매핑 정보를 비교하여 변경된 내용만 반영
validate : 엔티티와 테이블이 정상적으로 매핑되었는지만 확인
none : 사용하지 않음
엔티티 -> DTO,,
여기서는 update를 썼는데, create로 해두면 매번 새로 생긴다. jpa는 시작할 때마다(?)
살짝 오류가 생겨서
application.properties에서 수정
MariaDB103Dialect -> MariaDB106Dialect
#jpa 방언 설정
spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.MariaDB106Dialect
# update : 엔티티 매핑 정보를 비교하여 변경된 내용만 반영
spring.jpa.hibernate.ddl-auto=update
spring.jpa.properties.hibernate.format_sql=true
spring.jpa.properties.hibernate.show_sql=true
JPABoardService를 만든다(기존꺼랑 구분 위해서 JPA 붙임)
DTO같은 거고 중요하다.
package com.apple.web.entity;
import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.Id;
@Entity
public class JPABoard {
@Id
private int jbno;
@Column
private String jbtitle;
@Column
private String jbcontent;
}
PK가 @Id
그 외의 컬럼은 @Column
더 명확히 써준다면
import java.time.LocalDateTime;
import org.hibernate.annotations.ColumnDefault;
import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
@Entity
public class JPABoard {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private int jbno;
@Column(columnDefinition = "TEXT")
private String jbtitle;
@Column(columnDefinition = "LONGTEXT")
private String jbcontent;
@ColumnDefault("CURRENT_TIMESTAMP")
private LocalDateTime jbdate = LocalDateTime.now();
@ColumnDefault("1")
private int jbread;
@ColumnDefault("0")
private int jblike;
}
컬럼을 설정하면서 만들어준다는 느낌.
lombok getter, setter도 해준다.
이러고 application.properties
update -> create로 변경. 지우고 새로 만든다는 뜻.
spring.jpa.hibernate.ddl-auto=create
서버재실행
drop table if exists jpaboard 이 부분이 나온다.
Hibernate:
drop table if exists jpaboard
Hibernate:
create table jpaboard (
jblike integer default 0 not null,
jbno integer not null auto_increment,
jbread integer default 1 not null,
jbdate datetime(6) default CURRENT_TIMESTAMP,
jbcontent LONGTEXT,
jbtitle TEXT,
primary key (jbno)
) engine=InnoDB
application.properties 이거 혹시나해서 추가
# thymeleaf호출시 경로 설정
spring.thymeleaf.cache=false
spring.thymeleaf.prefix=classpath:/templates/
spring.thymeleaf.suffix=.html
도커
쿠버네티스
뷰
AWS
감사합니다