[    Eclipse환경에서 Run을 시켰을때 빌드를 통해 메모리를 확보하는 중간에 Eclipse가 응답없음으로 바뀌는 에러입니다. ]


'Unable to execute dex: GC overhead limit exceeded GC overhead limit exceeded'

해결방안)

이클립스에서 사용하는 메모리가 부족함으로

Eclipse폴더에 있는 eclipse.ini파일을 수정하면 됩니다.

-Xms40m
-Xmx384m

이와 같은 내용(메모리 사용 40/384)을

-Xms1024m
-Xmx1024m

혹은 그 이상으로 설정해줍니다.

[    http://partnerjun.tistory.com/51 님 블로그 내용을 포스팅한 것을 미리 밝힙니다. ]


1. Chrome 개발자도구 Network 탭

크롬에서 F12키나 Ctrl+Shift+I 혹은 메뉴의 '도구 더 보기'에서 열 수 있는 개발자도구는 아주 강력하다. HTML dom 탐색은 물론 javascript나 css 소스를 탐색하고 수정할 수 있을뿐 아니라 수정해 곧바로 적용해 볼 수 있다. 이번 포스트에서 주로 사용하는 Network탭에서는 실시간으로 Request / Response 정보를 확인 수 있다.



각 요청이 시작되기 전/후를 스크린샷으로 남기는 기능과 원하는 요청만 표시하는 필터같은 유용한 기능도 있다. 다양한 기능을 한번씩 사용해 보면 많은 도움이 된다.




2. Jsoup로 네이버 검색어 자동완성 목록 얻어오기

네이버 검색창에 단어를 입력했을 때 나오는 검색어 자동완성 목록을 Jsoup로 얻어보자.


이거.



먼저 크롬의 개발자 도구를 열어 두고 네이버 검색창에 단어를 입력해 보자. 키를 누를 때마다 Request/Response가 감지된다.





검색창에 입력할 때마다 특정 URL로 get Request가 있다는 사실을 알 수 있다. Response 탭을 이용해 Response를 확인해 보자.




뭔가 이상하다. 이건 무슨 코드일까?




다시 Request의 헤더를 보자. _callback 파라미터와 Response의 첫 부분이 같다는 사실을 알 수 있다. 


 


또한 window라는 Javascript Object와 __jindo_callback... 형식이 함수와 유사하다는 점을 통해


 클라이언트에서 _callback 파라미터로 함수의 이름을 전달하고, 

 서버에서 _callback 파라미터로 전달된 함수의 파라미터로 '결과' Json을 적어 반환해 

 클라이언트에서 결과 '문자열'을 실행하거나 정의하는 형식이라고 추측할 수 있다. 


그림으로 표현하면 아래와 같다.



그야말로 막연한 추측이다. 하지만 _callback 파라미터를 조정해 볼 필요는 있다. 한번 시도해 보자.



Chrome 확장프로그램 Advanced REST client로 테스트한 결과.

_callback 파라미터를 공백으로 요청하자 Json 형태로 결과를 얻을 수 있었다.



개인적인 경험상, 개발의 편의성 때문인지 HTML 코드를 그대로 반환하는 사이트가 가장 많고, 그 다음으로 위와 같이 Json과 다른 형식의 코드가 섞인 경우가 많았다. Json 포맷으로 Response가 오는 정직한 경우는 별로 없으니 얻어낸 문자열을 다시 가공하거나 위의 경우처럼 파라미터를 조정해 볼 필요가 있다.



아무튼, 위에서 알아낸 URL과 Request 헤더들을 이용해 Jsoup로 네이버의 검색어 자동완성 목록을 얻어내 보자.


String q = "스칼라"; // 검색어

Document doc = Jsoup.connect("https://ac.search.naver.com/nx/ac")
.header("origin", "http://www.naver.com")
.header("referer", "https://www.naver.com/")
.header("accept-encoding", "gzip, deflate, sdch, br")
.data("con", "1")
.data("_callback", "") // _callback 파라미터를 비우면 JSON이 리턴된다!
.data("rev", "4")
.data("r_enc", "UTF-8")
.data("q", q) // 임의로 몇개의 파라미터와 헤더를 생략했다.
.data("st", "100") // 각 파라미터가 무엇을 뜻하는지를 확인해 적절하게 사용하는 것도 좋지만
.data("q_enc", "UTF-8") // 비정상적인 요청으로 감지해 아이디나 아이피가 밴 될 우려도 있으므로
.data("r_format", "json") // 특별한 이유가 없다면 모두 포함하는 것이 좋다.
.data("t_koreng", "1")
.data("ans", "2")
.data("run", "2")
.ignoreContentType(true) // HTML Document가 아니므로 Response의 컨텐트 타입을 무시한다.
.get();

List<String> result = new ArrayList<>();

// org.json 라이브러리를 사용해 결과를 파싱한다.
JSONObject jsonObject = new JSONObject(doc.text());

JSONArray items = (JSONArray) ((JSONArray) jsonObject.get("items")).get(0);
for(int i=0; i<items.length(); i++) {
String item = (String) (((JSONArray) items.get(i)).get(0));
result.add(item);
}

// 얻어낸 추천 검색어 목록.
// 테스트 프로젝트의 자바 버전이 낮아 for문을 사용했다.
for(String item : result) {
System.out.println(item);
}
/*
스칼라티움 강남
스칼라티움
구글스칼라
스칼라
강남 스칼라티움
첼로 스칼라티 105
스칼라티움 상암
수원 스칼라티움
상암 스칼라티움
첼로 스칼라티
구리 스칼라티움
스칼라 동시성 프로그래밍
스칼라 월드 북스 3
스칼라 월드 북스 4
스칼라 월드 북스 5
*/


원하는 정보를 얻어냈다.(웨딩홀 이름이 가장 위라니 조금 슬프다)




이 예제에는 없었지만 XMLHttpRequest 객체를 사용하는 Request에는 'X-Requested-With' 헤더 값으로 'XMLHttpRequest'가 전송되기도 한다. 다시한번 말하지만 사이트마다 다르고 비정상적인 요청으로 간주될 수 있으니 브라우저에서 직접 헤더를 확인해 보고 Jsoup의 헤더에 똑같이 작성하는 것이 좋다.

[ http://partnerjun.tistory.com/43 님의 티스토리 글을 포스팅한 내용임을 미리 밝힙니다. ]


이 포스트에서는 로그인이 필요한 사이트와 Request Header를 검사하는 사이트를 파싱하는 과정을 적어둔다.



0. 웹 사이트 로그인

먼저 웹 사이트에 로그인에 대해 다시 생각해 볼 필요가 있다. 최근 웹 사이트에서 사용되는 로그인 방법은 크게 두 가지로 볼 수 있다. 첫 번째는 세션을 이용한 방법이고, 두 번째는 Restful API에 주로 사용되는 토큰 인증이다. 발급 받은 토큰을 이용하는 방법은 이전 포스트에서 원하는 값을 Jsoup의 Document를 파싱해 얻어낸 것처럼, 간단하게 얻어낼 수 있다. 물론 토큰이 HTML요소가 아니라 Script 요소로 있는 경우도 많지만 정규식이나 replace, split 같은 메소드를 이용하면 별 어려움이 없다.


다시 첫 번째 세션 로그인으로 돌아가면, 세션은 결국 쿠키라는 사실을 기억해야 한다. 상태를 유지하지 않는 HTTP 프로토콜의 특성 상 사이트에 로그인하는데 성공하면 서버는 클라이언트에게 세션ID를 발급해주고 

ID/PW는 Request에, 세션ID는 쿠키에 담겨 있다

 

클라이언트는 서버로부터 받은 세션ID를 다음 Request부터 쿠키에 포함해 전송하게 된다. 서버는 클라이언트가 전송한 쿠키에서 얻어낸 세션ID를 이용해 이 유저가 '로그인 한' 유저인지 여부를 확인할 수 있게 된다.


Response에도 당연히 쿠키가 포함되어 있다.

뭐 결국 간단히 말하자면 세션으로 로그인을 체크하는 사이트라면 로그인하고 얻은 쿠키를 다음 Request부터 계속 사용하면 된다는 말이다.


 

0. 사이트의 CSRF Token, Request Header

대부분의 유명한(사용자가 많은) 사이트에서는 비정상적인 접근을 막기 위해 여러가지 방법을 사용한다. 그 중 신경써야 할 것은 CSRF 토큰과 Request Header이다. 

 

CSRF 토큰은 로그인 시도 전에 한 가지 단계를 더 거치면 된다. 로그인을 처리하는 URL에 바로 요청하는 것이 아니라 '로그인 페이지' 에 접근해서 토큰을 얻어낸 후로그인 처리 URL에 토큰을 포함해 요청해야 한다. 티스토리를 예로 들어보자.

 

파란색으로 표시된 '눈에 보이는' 직접 입력하는 fieldset 외에도 

특수한 키가 적혀있는 파라미터 두 개가 있다.

 

스크린샷에서 볼 수 있는 두 가지 파라미터 "ofp"와 "nfp"처럼 로그인 페이지에 접속해야 얻을 수 있는 값이 있다. 때문에 먼저 '로그인 페이지'에 접근해 저런 값들을 얻어낸 후, '로그인 처리 URL'에 보내는 데이터에 그 값들을 포함하면 된다.

 

Request Header는 HTTP 표준에 맞게 전송하는 것이 원칙이다. 정상적인 브라우저라면 따로 신경 쓸 필요가 없지만 Jsoup를 통한 접속에서는 신경써야 한다. 몇몇 사이트에서는 Request Header를 철저하게 검사해 접근을 막거나 아이디를 밴하기도 한다. 

Header값들을 얻는 가장 쉬운 방법은 브라우저로 직접 로그인 해 보고 헤더 값들을 모두 복사해 그대로 사용하는 것이다.


 

티스토리에 로그인할때 전송된 Request header

(Chrome 확장 프로그램 HTTP Headers를 이용함)

 

다른 값들은 그냥 넣는다고 해도 User-Agent만큼은 조금 신경 쓸 필요가 있다. 사용자의 브라우저를 확인하는 값이기 때문이다. 이 값을 모바일 브라우저로 변환한다면 모바일 페이지를 따로 사용하는 사이트에서는 모바일 페이지로 리다이렉트된다. 만약 얻어내고자 하는 값이 모바일 화면의 값이라면 적절한 User-Agent를 하나 구해 사용하자.(기종명 User Agent로 검색하면 다 나온다)




그럼 이제 티스토리에 로그인하고 블로그 관리 페이지에서 내 블로그 목록을 얻어내는 코드를 만들어 보자.


select의 option값


1. 티스토리 로그인 페이지에 접속해 토큰 얻어내기

위에서 본 것처럼 티스토리에서는 로그인에 두 가지 토큰을 발급받아 전송한다. 각각이 무슨 의미인지는 모르겠지만 일단 가져와 보자.

// 로그인 페이지 접속
Connection.Response loginPageResponse = Jsoup.connect("https://tistory.com/auth/login/")
.timeout(3000)
.header("Origin", "http://tistory.com/")
.header("Referer", "https://www.tistory.com/auth/login")
.header("Accept", "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8")
.header("Content-Type", "application/x-www-form-urlencoded")
.header("Accept-Encoding", "gzip, deflate, br")
.header("Accept-Language", "ko-KR,ko;q=0.8,en-US;q=0.6,en;q=0.4")
.method(Connection.Method.GET)
.execute();

// 로그인 페이지에서 얻은 쿠키
Map<String, String> loginTryCookie = loginPageResponse.cookies();

// 로그인 페이지에서 로그인에 함께 전송하는 토큰 얻어내기
Document loginPageDocument = loginPageResponse.parse();

String ofp = loginPageDocument.select("input.ofp").val();
String nfp = loginPageDocument.select("input.nfp").val();

첫 번째 포스트와 마찬가지지만 필요한 헤더를 작성하고 get() 이나 post() 메소드가 아니라 execute() 메소드를 이용해 Document보다 상위 객체인 Response 객체를 얻어왔다. Response 객체의 cookies() 메소드를 이용해 쿠키를 얻어내고, parse() 메소드로 Document를 얻어낸 후 Document에서 두 가지 토큰을 가져왔다. 

티스토리는 로그인 페이지에 접근하기만 해도 뭔지 모를 쿠키들을 전송해 주기 때문에 로그인 페이지에서부터 쿠키를 가져왔다.



2. 로그인하고 로그인 세션ID 얻어내기

먼저 로그인을 처리하는 URL, 즉 form의 action과 method, 전송할 값들을 알아내야 한다.


form의 method와 action(로그인 처리 URL)


전송해야 하는 파라미터는 "redirectUrl", "loginId", "loginPw", "rememberLoginId"와 

토큰 "ofp", "nfp" 총 여섯 개다.

티스토리는 아주 정직하게 태그에 표시되어 쉽게 알 수 있지만 어떤 사이트는 자바스크립트로 어지럽게 작성되어 있다. 그런 경우는(특히 js가 압축된 경우!) 크롬 개발자도구의 Network 탭을 이용하면 편하다.


위에서 확인한 파라미터를 이용해 Jsoup Connection의 데이터로 추가하고 post로 요청하면 '로그인 된' 세션ID를 얻어낼 수 있다.

// Window, Chrome의 User Agent.
String userAgent = "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/57.0.2987.133 Safari/537.36";

// 전송할 폼 데이터
Map<String, String> data = new HashMap<>();
data.put("loginId", "아이디");
data.put("password", "비밀번호");
data.put("rememberLoginId", "1");
data.put("redirectUrl", "http://tistory.com/");
data.put("ofp", ofp); // 로그인 페이지에서 얻은 토큰들
data.put("nfp", nfp);

// 로그인(POST)
Connection.Response response = Jsoup.connect("https://www.tistory.com/auth/login")
.userAgent(userAgent)
.timeout(3000)
.header("Origin", "http://tistory.com/")
.header("Referer", "https://www.tistory.com/auth/login")
.header("Accept", "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8")
.header("Content-Type", "application/x-www-form-urlencoded")
.header("Accept-Encoding", "gzip, deflate, br")
.header("Accept-Language", "ko-KR,ko;q=0.8,en-US;q=0.6,en;q=0.4")
.cookies(loginTryCookie)
.data(data)
.method(Connection.Method.POST)
.execute();

// 로그인 성공 후 얻은 쿠키.
// 쿠키 중 TSESSION 이라는 값을 확인할 수 있다.
Map<String, String> loginCookie = response.cookies();

이제 로그인에 성공했다. 얻어낸 이 '로그인 된' 쿠키를 계속 사용하면 된다. 세션ID의 키는 서버사이드 설정에 따라 언어의 기본 값(PHP는 PHPSESSID, JSP는 JSESSIONID 등)이거나 따로 지정한 이름이다. 딱히 중요한 내용은 아니지만 서버사이드 언어를 유추하는 방법 중 하나가 된다.



3. 티스토리 블로그 관리 페이지에서 내 블로그 목록 얻어내기

위에서 얻은 쿠키를 사용한다는 점 외에는 이전 포스트와 차이가 없다. 접속하고 값을 얻어내면 된다. 

// 티스토리 관리자 페이지
Document adminPageDocument = Jsoup.connect("http://partnerjun.tistory.com/admin")
.userAgent(userAgent)
.header("Referer", "http://www.tistory.com/")
.header("Accept", "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8")
.header("Content-Type", "application/x-www-form-urlencoded")
.header("Accept-Encoding", "gzip, deflate, sdch")
.header("Accept-Language", "ko-KR,ko;q=0.8,en-US;q=0.6,en;q=0.4")
.cookies(loginCookie) // 위에서 얻은 '로그인 된' 쿠키
.get();

// select 내의 option 태그 요소들
Elements blogOptions = adminPageDocument.select("select.opt_blog > option");

// 블로그 이름과 url 얻어내기
for(Element option : blogOptions) {
String blogName = option.text();
String blogUrl = option.attr("abs:value");

System.out.println(blogName); // 간단한 블로그
System.out.println(blogUrl); // http://partnerjun.tistory.com/admin/center/
}


최근 많은 사이트에서는 보안 목적으로 로그인 후에 추가적인 과정을 요구하기도 한다. 대표적인 것은 새로운 기기에서의 로그인 시 이메일 체크나 capcha다. 이메일 체크야 직접 한번 해주면 되지만 capcha는 아직 만만한 문제가 아니다. 기계학습을 이용해 capcha를 해결하는 방법이 나왔다는 이야기를 들었는데 얼마 후면 라이브러리로 제공될지도 모르겠다. 기술의 발전을 기뻐해야 하는 건지, 개발하는 측에 있는 사람으로써 두려움에 떨어야 하는지는 모를 일이다.


아무튼, Jsoup로 로그인하고 '로그인 한' 사용자만 접근 가능한 페이지의 값을 얻어내 보았다. 다음 포스트에서는 XMLHttpRequest 객체를 이용한 Ajax 요청을 Jsoup로 해 보려고 한다(사실 특별한 내용은 없지만 크롬 개발자도구의 Network탭 그림 때문에 분리한다).

[ 해당 포스팅은 http://partnerjun.tistory.com/guestbook 님 티스토리 블로그 내용을 포스팅 한 것임을 밝힙니다. ]



Jsoup는 아주 강력하고 재미있는 라이브러리다. 단순한 HTML 문서 파싱을 넘어 웹 사이트에 대한 Request, Response를 모두 처리할 수 있다. 덕분에 일부 특별한 경우(플래시, 애플릿, ActiveX같은 비표준이나 WebSocket)가 아니라면 브라우저로 사이트를 이용하는 상황을 그대로 재현해낼 수 있다. 다시 말해, 대부분의 사이트의 원하는 정보만 뽑아내는 '뷰어'를 만들 수 있다는 것이다. 몇 가지 간단한 예제를 통해 사이트에서 원하는 정보만 뽑아내는 과정을 적어보려 한다.



0. Gradle 디펜전시 추가

compile group: 'org.jsoup', name: 'jsoup', version: '1.10.2'

Maven Repository를 통해 간단하게 디펜전시를 추가 할 수 있다.


Jsoup는 크게 static 메소드를 체이닝해서 URL(혹은 로컬HTML)에 연결하고 결과를 얻어오는 org.jsoup.Jsoup 패키지와 얻어온 결과의 구조를 위한 객체들이 포함된 org.jsoup.nodes 패키지, 연결 방법과 Response, Request등을 가지고 있는 org.jsoup.Connection 패키지로 이루어져 있다. 



Jsoup의 주요 요소는 크게 다섯 가지로 볼 수 있다.


Document 

 Jsoup 얻어온 결과 HTML 전체 문서

Element

 Document의 HTML 요소

Elements

 Element가 모인 자료형. for나 while 등 반복문 사용이 가능하다.

Connection

 Jsoup의 connect 혹은 설정 메소드들을 이용해 만들어지는 객체, 연결을 하기 위한 정보를 담고 있다.

Response

 Jsoup가 URL에 접속해 얻어온 결과. Document와 다르게 status 코드, status 메시지나 charset같은 헤더 메시지와 쿠키등을 가지고 있다.




Jsoup로 하는 작업은 크게 Connection 객체를 통해 URL에 접속하고(혹은 로컬 파일/문자열), Response 객체에서 세션ID같은 쿠키와 HTML Document를 얻어낸 후, Document의 Element들을 파싱하는 과정으로 나누어진다고 볼 수 있다.




1. URL 접속해 결과 얻어오기

URL에 접속해 Document를 얻어내기는 아주 쉽다.

// 간략화된 GET, POST
Document google1 = Jsoup.connect("http://www.google.com").get();
Document google2 = Jsoup.connect("http://www.google.com").post();

// Response로부터 Document 얻어오기
Connection.Response response = Jsoup.connect("http://www.google.com")
.method(Connection.Method.GET)
.execute();
Document google3 = response.parse();

http://www.google.com에 접속하는 방법들



얻어낸 Document는 두가지 방법으로 출력할 수 있다. .html() 메소드와 .text() 메소드 두 가지다.

Connection.Response response = Jsoup.connect("http://www.google.com")
.method(Connection.Method.GET)
.execute();
Document document = response.parse();

String html = document.html();
String text = document.text();

html과 text는 JQuery의 메소드와 유사하다. 문서의 html 그 자체를 가져올지, html 태그 사이의 문자열만을 가져올지를 택하는 것이다.



document.html()의 결과

<!doctype html>

<html itemscope itemtype="http://schema.org/WebPage" lang="ko">

 <head>

  <meta content="/images/branding/googleg/1x/googleg_standard_color_128dp.png" itemprop="image">

  <link href="/images/branding/product/ico/googleg_lodp.ico" rel="shortcut icon">

  <meta content="origin" id="mref" name="referrer">

  <title>Google</title> 

  <script>(function(){window.google={kEI:'P7QKWeJtioTzBfTmg6AB',kEXPI:'1352553,3700294,3700347,4029815,4031109,4032677,4036527,4038214,4038394,4039268,4041776,4043492,4045096,4045293,4045841,4046904,4047140,4047454,4048347,4048980,4050750,4051887,4056126,4056682,4058016,4061666,4061980,4062724,4064468,4064796,4065786,4069829,4071757,4071842,4072270,4072364,4072774,4076095,4076999,4078430,4078588,4078763,4080760,4081038,4081165,4082131,4082230,4083046,4090550,4090553,4090806,4091353,4092934,4093313,4093498,4093524,4094251,4094544,4094837,4095910,4095999,4096323,4097150,4097922,4097929,4098096,4098458,4098721,4098728,4098752,4100169,4100174,4100376,4100679,4100714,4100828,4101376,4101429,4101750,4102028,4102032,4102107,4102238,4103215,4103254,4103475,4103845,4103849,4103999,4104202,4104204,4104527,4105085,4105099,4105178,4105317,4105470,4105788,4106085,4106209,4106949,4107094,4107221,4107395,4107422,4107525,4107555,4107628,4107634,4107807,4107895,4107900,4107957,4107966,4107968,4108012,4108016,4108027,4108033,4108417,4108479,4108537,4108539,4108553,4108687,4108885,4109075,4109293,4109316,4109489,4109498,4110094,8300508,8503585,8508113,8508229,8508931,8509037,8509373,8509826,10200083,10200096,19001874,19002112,19002127,41027342',authuser:0,j:{en:1,bv:24,pm:'p',u:'c9c918f0',qbp:0},kscs:'c9c918f0_24'};google.kHL='ko';})();(function(){google.lc=[];google.li=0;google.getEI=function(a){for(var b;a&&(!a.getAttribute||!(b=a.getAttribute("eid")));)a=a.parentNode;return b||google.kEI};google.getLEI=function(a){for(var b=null;a&&(!a.getAttribute||!(b=a.getAttribute("leid")));)a=a.parentNode;return b};google.https=function(){return"https:"==window.location.protocol};google.ml=function(){return null};google.wl=function(a,b){try{google.ml(Error(a),!1,b)}catch(c){}};google.time=function(){return(new Date).getTime()};google.log=function(a,b,c,d,g){a=google.logUrl(a,b,c,d,g);if(""!=a){b=new Image;var e=google.lc,f=google.li;e[f]=b;b.onerror=b.onload=b.onabort=function(){delete e[f]};window.google&&window.google.vel&&window.google.vel.lu&&window.google.vel.lu(a);b.src=a;google.li=f+1}};google.logUrl=function(a,b,c,d,g){var e="",f=google.ls||"";c||-1!=b.search("&ei=")||(e="&ei="+google.getEI(d),-1==b.search("&lei=")&&(d=google.getLEI(d))&&(e+="&lei="+d));a=c||"/"+(g||"gen_204")+"?atyp=i&ct="+a+"&cad="+b+e+f+"&zx="+google.time();/^http:/i.test(a)&&google.https()&&(google.ml(Error("a"),!1,{src:a,glmm:1}),a="");return a};google.y={};google.x=function(a,b){google.y[a.id]=[a,b];return!1};google.lq=[];google.load=function(a,b,c){google.lq.push([[a],b,c])};google.loadAll=function(a,b){google.lq.push([a,b])};}).call(this);

google.j.b=(!!location.hash&&!!location.hash.match('[#&]((q|fp)=|tbs=rimg|tbs=simg|tbs=sbi)'))

||(google.j.qbp==1);(function(){google.hs={h:true,pa:true,q:false};})();(function(){goo


(이하 생략)



document.text()의 결과

Google 스크린 리더 사용자는 여기를 클릭하여 Google 순간 검색을 설정 해제하시기 바랍니다. Gmail 이미지 로그인 Google 순간 검색을 사용할 수 없습니다. 검색어를 입력한 후 Enter를 누르세요. 자세히 알아보기 Google연결 속도 문제로 순간 검색이 중지되었습니다. 검색하려면 Enter를 누르세요. 검색하려면 Enter를 누르세요. 부적절한 예상 검색어 신고 × 한국 'Ok Google'이라고 말하면 음성 검색이 시작됩니다. 손가락 하나 움직이지 않고 검색해 보세요. 'Ok Google' 다음에 말한 내용을 Chrome에서 검색합니다. 자세히 알아보기아니요'Ok Google' 사용 개인정보처리방침 약관 설정 검색 설정 고급검색 기록 검색 도움말 의견 보내기 Google.com 사용 광고 비즈니스 Google 정보 내 계정 검색 지도 YouTube Play 뉴스 Gmail 드라이브 캘린더 Google+ 번역 사진더보기 문서 도서 Blogger 주소록 행아웃 KeepGoogle 제품 모두 보기



이 두가지 메소드는 Document뿐 아니라 Element에도 구현되어 있다. 



2. 얻어온 결과에서 특정 값 뽑아내기

특정 값, 그러니까 특정한 html 요소를 얻으려면 select("css query") 메소드를 사용하면 된다. 

구글 메인 페이지 검색 버튼의 value를 얻어 보자.


검색 버튼의 name은 btnK다.


Connection.Response response = Jsoup.connect("http://www.google.com")
.method(Connection.Method.GET)
.execute();
Document googleDocument = response.parse();
Element btnK = googleDocument.select("input[name=btnK]").first();
String btnKValue = btnK.attr("value");

System.out.println(btnKValue); // Google 검색

select의 결과는 Elements다. 그 중 첫번째 Element를 first() 메소드로 선택했다.





※ 목표가 있는 예제

불법만화로 유명한 그 사이트(머루)의 뷰어를 만든다고 상상해보자.

얻어내야 할 값은 크게 두 가지다.


1. 만화의 목록

2. 만화의 이미지 파일


이 값들을 얻어내기 위해서는 

1) 만화 목록을 얻어낸다. 

2) 글 내용에서 실제 만화 이미지가 있는 링크를 얻어낸다.

3) 이미지가 있는 링크에 접속한 후 이미지를 뽑아낸다. 

이렇게 세 가지 과정으로 진행해 보자.



1) 만화 목록 얻어내기

앞서 살펴본 Jsoup의 Conenction 메소드를 이용해 '업데이트' 페이지에 접속해 Doucment를 얻어낸다.

Document rawData = Jsoup.connect(URL)
.timeout(5000)
.get();

이 불법적이고 무서운 사이트는 Request Header를 검사하지 않는다. 그래서 위 코드처럼 아무런 추가적인 정보 없이 간단하게 결과를 얻어 올 수 있다. 하지만 Request를 철저하게 검사하는 사이트에는 더 많은 정보가 필요하다. 그런 사이트는 다음 글에 적을 예정이다.



아무튼, 이제 얻어낸 Document에서 정보를 뽑아낼 차례다. 구글 크롬의 개발자 도구를 이용해 업데이트 페이지를 확인해 보자.


'업데이트' 페이지 HTML


게시판은 table 태그를 사용하고, 각 행은 tr 태그에 매칭되며 공지사항은 tr_notice 클래스를 가지고 있다는 사실을 알 수 있다. 




tr 태그의 내부


tr 태그에 포함된 요소들을 살펴보자. 

a 태그로 글 내용에 해당하는 url을 얻을 수 있고, a태그의 첫번째 div에서 제목을 얻을 수 있다. 마지막으로 small 태그를 통해 글이 작성된 날짜를 얻을 수 있다. 


이렇게 얻어낸 사실들을 직접 코드로 구현하자.

Elements articles = rawData.select("tr:not(.tr_notice) a"); // 공지사항을 제외한 tr의 a 태그들을 얻어온다.

for(Element article : articles) {

String href = article.attr("abs:href"); // a태그 href의 절대주소를 얻어낸다.

// a 태그 안에 포함된 div들
Elements articleDiv = article.select("div");

String thumbUrl = ROOT_URL
+ articleDiv.first() // 첫 번째 div에서 썸네일 url을 얻어온다.
.attr("style")
.replace("background-image:url(", "")
.replace(")", "");

String title = articleDiv.get(1).ownText(); // 두 번째 div에서 제목을 얻어낸다.

String date = articleDiv.get(1).select("small").text()
.split("\\|")[0];

System.out.println(href); // http://ma../b/mangup/00000
System.out.println(thumbUrl); // http://ma../quickimage/...
System.out.println(title); // 제목
System.out.println(date); // 날짜
}

얻어내고자 한 요소들을 css 선택지로 얻어낸 후, split이나 replace등의 메소드를 이용해 정리한다.


이 '글 목록'에 해당하는 정보는 필요에 맞게 정의한 객체에 담아 보관하거나 유저에게 보여 줄 수 있다.




2) 글 내용에서 만화가 있는 링크 얻어내기

위에서 얻어낸 글 내용 url에 접속한 후, 실제 이미지가 있는 페이지에 접근할 차례다. 태그 분석을 위해 브라우저로 페이지에 직접 들어가 보자.


글 내용 HTML


글의 내용에 해당하는 div(#vContent)의 첫 번째 a 태그의 href 속성이 실제 만화 이미지가 포함된 URL이다.



Document rawData = Jsoup.connect(ARTICLE_URL)
.timeout(5000)
.get();

Elements contentATags = rawData.select("#vContent a"); // 공지사항을 제외한 tr의 a 태그들을 얻어온다.

String viewPageUrl = contentATags.first()
.attr("abs:href"); // 마찬가지로 절대주소 href를 얻어낸다

System.out.println(viewPageUrl); // http://wasabi.../archives/XXXXX...

아주 간단하게 이미지들이 포함된 주소를 얻어낼 수 있다. 




3) 만화 이미지가 있는 URL에 접속해 이미지 URL 얻어내기

마찬가지로 만화 이미지가 포함된 URL에 접속해 태그를 분석한다.


만화 이미지가 있는 페이지의 HTML


html 코드를 보면 이미지들이 가진 특정 클래스가 있다. 이 클래스를 가진 img 태그들을 얻어낸 후, data-src 속성을 뽑아내자.


  Document rawData = Jsoup.connect(VIEWER_URL)
.timeout(5000)
.get();

Elements imgs = rawData.select("img[class=lz-lazyload]"); // lz-lazyload 클래스를 가진 img들

List<String> imageUrls = new ArrayList<>();

for(Element img : imgs) {
imageUrls.add(img.attr("abs:data-src"));
}

System.out.println(imageUrls); // 이미지 URL들.
}


만화 내용이 되는 모든 이미지 URL을 뽑아넀다. 이 URL에 접속해 직접 파일로 다운로드 할 수도, 자기 나름의 뷰어에 출력 할 수도 있다. 또, 목록을 얻어 낼 때 필요한 data들을 포함함으로써 원하는 페이지나 검색까지 구현이 가능하다.



[    웹개발(JAVA,JSP,Spring) 관련 예상 면접 질문    ]




웹 개발 면접시 인성 질문, 자기소개, 프로젝트 내용 질문을 제외한 JAVA와 관련된 기술 예상 면접 질문을 정리해 보았습니다.



1) 자바의 특징에 대해 말해보시오.

1) OOP(객체 지향 언어) 

: 부품에 해당하는 객체들을 먼저 만들고, 이것들을 하나씩 조립해 전체 프로그램을 완성하는 개발 기법

2) "가비지 컬렉션"에 의한 메모리 자동 관리

3) "멀티 쓰레드"를 지원한다.

4) JVM 위에서 동작하기 때문에 특정 OS에 종속적이지 않고 이식성이 좋으며, 보안성이 좋다.

5) 다양한 Open 라이브러리들이 존재한다.



2) 자바를 만든 사람에 대해 아시나요?

: "제임스 고슬링"



3) 변수란?

: "하나의 값을 저장할 수 있는 메모리 공간"



4) 객체와 클래스의 차이점에 대해 설명해 보시오.

- 클래스(Class) : 현실 세계의 객체의 속성과 동작을 추려내 필드와 메서드로 정의한 것으로 "아직 메모리가 할당되지 않은 상태"

vs

- 객체(Object) : 이 Class라는 설계도를 기반으로 실제 메모리가 잡힌 것을 의미하며 이런 객체를 조합해 전체 프로그램을 완성해

   나가는 방식을 OOP(객체지향 프로그래밍)이라고 한다.



5) 객체 지향 PG이란? 또 그 특징은?

: 현실세계의 객체를 필드와 메서드로 정의한 Class를 기반으로 실제 메모리가 잡혀 만들어진 부품과 같은 객체들을 조합해

  전체 프로그램을 완성해 나가는 개발 기법으로

특징)

- 캡슐화, 은닉화 : 외부 객체에서 구현방식은 알 수 없도록 숨기고 별도로 접근할 수 있는 getter/setter 메서드를 통해 접근하도록 하는 방식

- 상속 : 부모 Class를 자식이 접근할 수 있도록 물려 받는 방식

- 다형성 : 부모 클래스 타입으로 해당 부모를 상속받는 여러 자식 class를 대입할 수 있는 성질

등을 들 수 있다.



6) 다형성이란?

: 서로 다른 클래스로부터 만들어진 객체지만 같은 부모의 Class 타입으로 이들을 관리할 수 있는(=대입될 수 있는) 성질



7) 자바의 메모리 영역(간단하게 설명)

1. 메서드 영역 : static 변수, 전역변수, 코드에서 사용되는 Class 정보 등이 올라간다.

    , 코드에서 사용되는 class들을 로더로 읽어 클래스별로 런타임 필드데이터, 메서드 데이터 등을 분류해 저장한다.

2. 스택(Stack) : 지역변수, 함수(메서드) 등이 할당되는 LIFO(Last In First Out) 방식의 메모리

3. 힙(Heap) : new 연산자를 통한 동작할당된 객체들이 저장되며, 가비지 컬렉션에 의해 메모리가 관리되어 진다.



8) 추상메서드? 추상 클래스?

- 추상메서드 : 메서드의 정의부만 있고 구현부는 있지 않은 메서드

- 추상 클래스 : 추상메서드를 적어도 하나 이상 가지고 있는 클래스로 자식클래스에서 오버라이딩(재정의)가 필요한 추상메서드를 가지고 있기

        때문에 객체화 할 수 없다.



9) 인터페이스(Interface)란? 또 왜 사용하나?

: 인터페이스는 모든 메서드가 구현부가 없는 추상메서드로 이루어진 클래스로, abstract 키워드를 붙이지 않아도 자동으로 모든 메서드는 추상메서드로

  정의가 된다. 또한 변수도 자동으로 final static 키워드가 붙게 된다.


왜 인터페이스를 사용하는가? 

: 팀작업시 개발코드 부분과 객체가 서로 통신하는 접점 역할을 지원하게 되는데, 이는 개발코드에선 객체의 내부 구조를 모르더라도 인터페이스의

  메서드 명만 알고 있으면 되기 때문이다. 이를 통해 얻을 수 있는 장점은 해당 메서드를 통해 나오는 결과물을 알고 있기 때문에 다른 팀의

  작업을 기다리고 있지 않아도 되며, 또한 해당 객체가 수정될 경우 개발 코드 부분은 수정을 하지 않아도 된다.

  또한, 부가적으로 객체를 파일에 쓰기 위해 Serializable 인터페이스를 구현하거나, Collections.sort()를 하기 위해서 Comparable 인터페이스를 

  상속하는 것, Cloneable 을 구현하는 것처럼 특정 작업을 하겠다라는 "Mark"역할을 해주기도 한다.



10) 프로세스(Process) 와 쓰레드(Thread)의 차이점에 대해 아는가?

- 프로세스 : OS가 메모리 등의 자원을 할당해준 실행중인 프로그램을 가리킨다. 이때, 각각의 프로세스는 서로 메모리 공간을 독자적으로 갖기 때문에

    서로 메모리 공간을 공유하지 못한다. 따라서 공유하기 위해서는 IPC(InterProcess Communication)과 같은 방식이 필요하다.

- 쓰레드 : 쓰레드는 프로세스 내에서 프로세스의 자원을 가지고 실제로 일하는 "일꾼"과 같으며 각 쓰레드는 독자적인 Stack 메모리를 갖고 그 외의

  자원(메모리)는 프로세스 내에서 공유하게 된다.



11) 컬렉션프레임워크(CollectionFramework)에 대해 아는만큼 말해 보시오.


- Collection 인터페이스 

- List 인터페이스 : 배열과 유사하되, 추가할때마다 자동으로 Boundary를 늘려주는 구조로, 중복된 데이터를 허용하며, 순서가 존재한다.

ex) - ArrayList : 배열로 구현됬으며, 인접해 있기 때문에 데이터 조회에 매우 빠르다 하지만, 빈번한 삽입, 삭제시 새로 배열을 만들고 데이터를

옮겨야 하기 때문에 LinkedList에 비하여 속도가 느리다.

    - LinkedList : 링크 구조로 되어 있기 때문에 조회는 ArrayList에 비해 느리지만, 삽입 삭제시 링크를 끊고 새로 추가되는 데이터에 링크만

연결하면 되기 때문에 삽입, 삭제에 유리하다.

    - Vector : 구현 방식은 ArrayList와 유사하지만 Vector를 개선한 것이 ArrayList이다. 또한 Vector의 경우에는 ArrayList와 달리

Synchronized(동기화)가 걸려 있어 여러 쓰레드에서 동시에 접근할 수 없다.

- Set 인터페이스 : 집합처럼 중복된 데이터를 허용하지 않으며, 순서가 없다. 또한, 객체 내부의 중복된 데이터를 배제하고 싶은 경우

  Object 클래스의 equals 메서드와 hashCode 메서드의 재정의가 반드시 필요하다.

ex) - HashSet

    - TreeSet : 순서가 있는 HashSet으로 이진 트리 구조로 만들어 졌다. 순서에 맞게 정렬되어 저장되기 위해서 Comparable을 구현해야한다.

    


- Map 인터페이스 : key와 value 쌍으로 데이터를 저장하며, key는 중복될 수 없고, value는 중복 저장이 가능하다.

ex) - HashMap

     - TreeMap

     - Properties : key value 쌍으로 저장되지만 value의 타입이 String만 가능하다.

     - Hashtable : HashMap과 구조는 같으며, 단지 Synchronized(동기화) 되어져 있다는 점이 다른점이다.


12) 쿠키(Cookie)와 세션(Session)의 공통점과 차이점은?

- 공통점 : 둘 다 사용자의 데이터를 저장한다.

- 차이점

- 쿠키:  쿠키는 Client 컴퓨터에 저장했다 서버 요청시 네트워크를 타고 서버로 전달되기 때문에 보안에 취약하다.

- 세션 : 세션은 서버에 저장되고 브라우저 단위로 관리된다. 캐시에 비해 보안성이 좋다.


13) Request 전송 방식에는 어떤 것들이 있는지 아시나요?

- Get 방식 : URL의 쿼리문자열에 데이터를 같이 전달하는 방식으로 데이터 길이에 제한이 있고, 보안에 취약하다.

- POST 방식 : 헤더에 데이터를 넣어 보내기 때문에 보안에 조금 더 유리하고 데이터 길이에 제한이 없다. 하지만, Get에 비해 다소 느리다.

- DELETE 방식 : RESTFUL에서 삭제 기능을 할 때 주로 사용된다.

- PUT/PUSH 방식 : RESTFUL에서 수정 작업을 할 때 주로 사용된다.



14) RESTFUL이란?

: 해당 URL만 보더라도 바로 어떤 작업을 하는지를 알 수 있도록 하나의 데이터는 하나의 URL을 갖도록 작업하는 방식



15) Spring에서 DI란 무엇인지 아시나요?

: DI는 Dependency Injection(의존성 주입)의 약자로, 객체들 간의 의존성을 줄이기 위해 사용되는 Spring의 IOC 컨테이너의 구체적인 구현 방식입니다.

  DI는 기존처럼 개발코드 부분에서 객체를 생성하는 것이 아니라, 팩토리 패턴처럼 객체의 생성과, 데이터를 주입만 담당하는 Factory에 해당 하는 

 별도의 공간에서 객체를 생성하고 데이터간의 의존성을 주입해 개발코드에서는 이를 가져다 씀으로서 의존성을 줄이는 방식입니다. 이때, 

 Factory 패턴의 Factory Class의 역할을 스프링의 환경설정 파일이 담당합니다.



16) Spring의 AOP란?

: AOP는 Aspect Oriented Programming 관점 지향 프로그래밍의 약자로, 기존의 OOP(객체 지향 프로그래밍)에서 기능별로 class를 분리했음에도 불구하

 고, 여전히 로그, 트랜잭션, 자원해제, 성능테스트 메서드 처럼 공통적으로 반복되는 중복코드가 여전히 발생하는 단점을 해결하고자 나온 방식으로

 이러한 공통 코드를 "횡단 관심사"라 표현하며 개발코드에서는 비지니스 로직에 집중하고 실행시에 비지니스 로직 앞, 뒤 등 원하는 지점에

 해당 공통 관심사를 수행할 수 있게 함으로서 중복 코드를 줄일 수 있는 방식입니다.



17) Filter와 Interceptor 방식의 차이?



18) 디자인 패턴 아는 것?

1) 싱글톤(SingleTone Pattern) : 대표적으로 Calendar 객체나 dataSource 객체처럼 객체가 하나만 생성되어야 하는 경우

 전체 코드에서 하나의 객체만 존재할 수 있도록 이미 생성된 객체가 있으면 그 객체를 사용하도록 하는 방식입니다.

2) 팩토리 패턴(Factory pattern) : 객체간 의존성을 줄이기 위해 객체의 생성과 데이터 주입만 담당하는 Factory Class를 정의하고 개발 코드 부분에서는

   생성된 객체를 가져다 사용함으로서 의존성을 줄이는 방식입니다.

3) 옵저버 패턴(Observer Pattern) : 기후 정보처럼 RSS 수신시 하나의 객체가 변하면 다른 객체에 객체가 변했다는 사항을 알려주어야 할 경우에 주로

    사용됩니다.



19) MVC 패턴이란?

- Model : data 처리와 접근을 담당

- View : Client에 보여지는 화면을 담당

- Controller : Model과 View를 제어

하는 3가지 부분으로 나눔으로서, 데이터와 화면간의 의존관계를 벗어날 수 있게하는 개발 기법입니다.



20) 프로젝트 개발 순서??

대강...

1) 요구사항 분석 

기획 및 스토리 보드 작성

2) WBS(Work Breakdown Structure) 작성 : 작업 분해도로 프로젝트 범위와 최종산출물을 세부요소로 분할한 계층적 구조도

3) 논리 ERD 작성

4) 물리 ERD 작성

5) 개발

6) Testing

7) 유지보수



21) 오버로딩과 오버라이딩의 차이?

- 오버로딩 : 메서드 명은 동일하지만, 매개 변수 타입과 개수를 다르게 해 선언하는 방식

- 오버라이딩 : 상속한 자식에서 부모의 메서드를 재정의하는 방식


22) Servlet vs JSP

- Servlet : 자바 언어로 웹 개발을 위해 만들어진 것으로, Container가 이해할 수 있게 구성된 순수 자바코드로만 이루어진 것

- JSP : html 기반에 JAVA 코드를 블록화하여 삽입한 것으로 Servlet을 좀 더 쉽게 접근할 수 있도록 만들어 진 것


23) Wrapper Class의 사용이유를 아나요?

: 기본 data 타입은 객체가 아니어서 Object로 받는 다형성을 지원할 수가 없다. 하지만, 메서드에서 실재로 기본데이터 타입을 다형성으로

 넘겨주어야 하는 경우가 빈번히 발생하는데 이때, 기본 데이터 타입을 객체로 변환시켜 전달하기 위해 사용되며

 최근에는 AUTO Boxing, AUTO UnBoxing이 지원된다.



24) DataBase에서 Index란?

: Table에 대한 동작 속도를 높여주는 자료구조로서 빠른 검색을 가능하게 해준다.



25) private, protected, public, default 제어자에 대해 설명해 보시오

- private : 같은 class 내부에서"만" 접근이 가능하다.

- public : 어디서든 자유롭게 접근이 가능하다.

- protected : 같은 class 내부 + 상속받은 자식에서는 부모 class에 접근이 가능하다.

- default : 아무 것도 선언하지 않은 경우로 같은 패키지 내부에서만 접근이 가능하다.



26) SI가 무엇을 하는 건지 알고 오셨나요?

: System Integration의 약자로 시스템 통합 사업으로 고객의 기존 전산시스템을 통합하거나 새로운 시스템을 구축하는 작업입니다.



27) SW 개발시 가장 비중을 크게 두어야 할 부분은 어디라고 생각하나요?

: Testing 부분입니다. 



28) 자바의 제네릭이란??

: 클래스 내부에서 사용할 데이터 타입을 인스턴스(객체) 생성시에 결정짓는 방식


-----------------------------------------------------------------------------------------------------------------------------------------

-> 여기까지는 제가 예상하는 면접 질문을 올리고 이 아래부터는 실제로 제가 기술면접 당시 받았던 질문들입니다.



[ 기술 면접 질문 ]

 

 

1. CVSSVN에 대해서 아는대로 설명해 보시오.

 

2. 64bit CPU32bit CPUOS적 관점에서의 차이를 설명해 보시오.

 

3. 프로세스와 쓰레드의 차이점에 대해서 설명해 보시오.( 메모리 구조 포함 )

 

4. ‘데드락이란 무엇이고 이를 해결하기 위한 방법을 설명해 보시오.

 

5. 변수 명명법이 중요한 이유에 대해서 설명하고 예를 들어 보시오.

 

6. 자바의 JVM의 역할에 대해서 설명해 보시오.

 

7. 자바의 특징에 대해서 말해 보시오.

 

8. Linux에서 톰캣 환경설정을 잡는 것에 대해 설명해 보시오.

 

9. WAS와 웹서버의 차이점은?

 

10. JqueryAjax에 대해 아는가?

 

11. 비동기와 동기 방식의 차이점에 대해서 말해보시오.(네트워크 동기,비동기 아님)

 

12. 개발시에 중요하다 생각하는 요소를 3가지 기술해 보시오.

 

13. 스프링의 MVC에 대해서 설명하시오.

 

14. AOP란 무엇이고 왜 사용하는지

 

15. ‘에자일방법론에 대해서 아는가?

 

16. 스프링 환경설정 혼자 잡을 수 있는가? 대강 어떻게 해야하는지 설명해 보시오.

 

17. 웹서버 내부 구동 방식에 대해 설명할 수 있는가?

 

18. 스프링 DI?

 

19. UML 그려본 적 있는가?

 

20. Node jsAngular JS를 사용해 본 적이 있는가?

 

21. 캐시와 세션의 공통점과 차이점에 대해 말해보시오.

 

22. 디자인 패턴 아는 것들만 간략히 설명해 보시오.

 

23. DataBase에서 index관련 질문이었는대 잘 모르겠어서..기억이..

 

24. 크롬이나 파이어폭스에서 개발도구를 사용해 디버깅을 해보았는가?

 

25. JDBC는 무엇인가?

 

26. 스프링을 사용하지 않고 MVCJSP에서 만들어 보았는가?

 

27. DB 옵티마이저에 대해 아는가?

[    CSS로 일정 길이 이상일 때 텍스트 줄임말 ...로 표시하기    ]




1. 가끔 일정 길이를 넘어가면 문자열이 ...으로 생략되듯이 표시되게 하고 싶거나


2. 혹은, textarea 내에서 문자열을 길게 입력하면 지정한 길이 이상일 때 자동으로 개행(줄바꿈) 되도록 처리하고 싶을 때가 있다.



먼저, 1번 일정 길이를 넘어가면 문자열이 ... 으로 생략되듯이 표시하게 하려면 다음의 조건이 필요하다.

1) width가 고정적일 것( 최대 표시될 너비를 꼭 지정되어야 한다.)

2) overflow: hidden; 을 사용해 영역을 감출 것

3) 아래줄로 내려가는 것을 막기위해 white-space: nowrap 등이 필요

4) 마지막으로, 반드시 block요소여야 한다. inline요소에는 적용이 안됨... 따라서 인라인 태그 요소일 경우 => display:block으로 지정해 주어야 함!

5) ...으로 표시하기 위해서 text-overflow:ellipsis 로 지정해야한다.


이를 적용하면

<style type="text/css">
  body p {

  display:block;
  overflow: hidden; 
  text-overflow: ellipsis;
  white-space: nowrap; 
  width: 100px;
  }
</style>

처럼 지정하면 된다.

-------------------------------------------------------------------------------------------

다음으로...

일정 길이 이상이면 자동으로 개행이 되도록 textarea에서 설정할 경우는 다음과 같이

white-space:normal; 를 써주면 된다. 역시 width가 지정되어야 한다.

[    무한스크롤 구현 (스크롤 마지막을 인식하는 이벤트 처리)    ]



페이스북같은 SNS 사이트를 보면, 스크롤이 끝부분에 위치하면 추가적으로 데이터(글)들이 나오는 것을 알 수 있다.


이처럼 처음부터 다 나오는 것이 아니라 스크롤 끝에 갔을 때 특정 작업을 하려면


다음과 같이하면 된다.


$(window).scroll(function() { // 스크롤 이벤트가 발생할 때마다 인식

       // 스크롤이 끝에 닿음을 인식

    if ( $(window).scrollTop() == $(document).height() - $(window).height() ) {

    }

});


이처럼 작업해주면 되는데

이렇게 스크롤이 끝에 닿음을 인식할 때마다, PagingDTO등을 이용해서 다음 페이지 값을 불러오도록 처리해 주는 것과 연게하면


페이징처리를 이용한 무한스크롤을 구현할 수 있다.


위의 코드에서 listAll(page,keywords) 부분이 page를 넘기고 끝에 닿을 때마다 페이지를 ++해서 다음페이지를 불러오도록 하는 부분이다.


listAll 등의 함수에 대해 추가적으로 필요하신 분들은 요청시 코드 드리겠습니다.



[    동적으로 생성된 태그에 이벤트를 걸려면 어떻게 해야할까??    ]




가끔 ajax로 가져온 데이터를 문자열로 조합해서 태그에 append하는 경우 이런경우는 태그가 미리 생성되어 있는 경우가 아니라 동적으로


생성된 경우이다. 이런 경우에 문자열로 만든 태그에다가 id나 class준 다음에 $(선택자).on("click",function(event){}) 방식을 사용해서 이벤트를 걸어도


이벤트가 걸리지 않음을 알 수 있다.


이런 경우 어떻게 해야할까??


이런 경우는 document 객체에서 잡아와서 이벤트를 걸어야한다.


ex) $(document).on("click","선택자",function(event){})


[    클릭시 특정 태그 위치로 스크롤을 이동시키기    ]







[    이벤트 발생시 a 태그의 이동기능을 동작하지 않게하고 처리하기    ]




아주 가끔 a태그를 클릭시 a태그 href 에 지정된 속성으로 이동을 막고, 처리가 필요한 경우에 어떻게하는지에 대한 포스팅입니다.



[    JQuery 애니메이션 관련 정리(fadeIn,fadeOut,hide,show,animate,stop)    ]




[    script 선언의 위치와 수행 순서에 대해서...     ]



<script type="text/javascript">

...

</script>


이런식으로 스크립트를 작성하게 되는데 이때, 이 스크립트 블록을 어디다 수행하는게 좋을까요??


어디든 상관은 없습니다. 하지만 주의해야하는 사항이 몇가지 있죠


예를들어, head 태그 안에서 script를 선언하고 body 안에 선언해 놓은 태그에 대한 작업을 한다고 해봅시다.


그러면 어떻게 될까요? 


해당 script가 수행되는 시점에서 body 태그는 아직 파싱되기 전이기 때문에 script에서 해당 태그가 무엇인지 알 수가 없게 됩니다.


따라서 그냥 null을 가지고 작업을하게되죠...


따라서, body 태그가 다 로딩되고 body 태그 안에서 제일 밑 부분에 작성할 스크립트를 위치시켜주는 것이 best일 것입니다.


<!DOCTYPE html>

<html>

  <head>

    <meta charset="utf-8">

    <title></title>

  </head>

  <body>

    <input type="text" name="" value="" id="in">

    <table>

      <tr>

        <td>dd</td>

      </tr>

    </table>


    <script type="text/javascript">

      document.getElementById("in").value = "modifyValue";

    </script>

// 이처럼 아래쪽에 위치시켜주는 것이 좋습니다. 이걸 만약 head 태그 내에 위치하거나 input 태그 위에 위치시킨다면

// 스크립트가 제대로 수행될 수 없겠죠?

  </body>

</html>

[    3. 일정주기마다 callback 함수를 실행하는 setInterval()에 대해서...    ]



Callback Function

  • 조건을 등록해 두고 그 조건을 만족한 경우, 나중에 호출되는 함수

시간을 기반으로 콜백함수를 호출하는 명령

setTimeout( function, time )

  • time 시간이 지난 경우 function 함수를 콜백하는 함수
  • time 은 millisecond (1/1000초) 단위
  • timerId를 반환함

clearTimeout( timerId )

  • setTimeout 함수 호출의 결과로 반환된 timerId를 인자로 받아 예약되어 있던 function호출을 취소
    • 이미 function이 호출된 경우(시간이 지나 이벤트가 발생한 경우)에는 효과 없음

setInterval( function, time )

  • time 시간이 경과할 때마다 function 함수를 콜백하는 함수
  • timerId를 반환함

clearInterval( intervalId )

  • setInterval 함수 호출의 결과로 반환된 intervalId를 인자로 받아 주기적으로 호출되던 function 호출을 취소


다음 예시 사진을 보면, setTimeout을 호출 했을 때 3초뒤에 callback()으로 지정한 함수가 실행된 것을 알 수 있습니다.


이때, 142를 반환했는데 이것은 setTimeout의 id라고 생각하시면 되고 지정한 setTimeout을 해제하기 위해서는 clearTimeout(setTimeout의 id)를 넣어주어야 취소가 가능합니다.


다음으로


setInterval ( 수행할 메서드, 시간) 으로 여기선 5초 마다 콜백 메서드를 수행하도록 되어 있습니다. 이것을 해제하기 위해서는 역시 callback 메서드의 id가 143번 임으로


clearInterval(143)을 통해 해제 시켜 준 것을 알 수 있습니다.


콘솔에서는 id가 몇번인지 알 수 있지만 코드상에서 작성할 때에는 바로바로 확인이 어려움으로


var setIntervalId = setInterval(callback, 5000);

clearInterval(setIntervalId); 처럼 해서 사용하시면 됩니다.



[    2. JQuery로 선택한 태그 요소의 값과 속성을 바까보자    ]



  <body>

    <textarea name="txtarea" rows="8" cols="80">

      <strong>안녕하세요</strong>

    </textarea>

    <input type="text" name="first" value="firstValue" />

    <input type="text" name="second" value="secondValue" style="color:red;">

    <a href="http://www.google.com" id="aTag">구글</a>

    <table id="tableTag">

      <tr class="hello">

        <td>안녕</td>

      </tr>

    </table>


    <script type="text/javascript">

      $(document).ready(function(){

        // 1. 태그 내의 텍스트 가져오기

        var textareaVal = $("textarea[name=txtarea]").text();

        // 텍스트를 가져오기 때문에 <strong> 태그도 문자열로 그대로 가져온다.

        console.log(textareaVal);


        // 2. 태그 내의 텍스트 변경하기

        $("textarea[name=txtarea]").text("값을 변경합니다.");


        // 3. input 태그 내의 value 값 가져오기

        var inputFirstVal = $("input[name=first]").value();

        console.log(inputFirstVal);


        // 4. input 태그 내의 value 값 바꾸기

        $("input[name=first]").value("value 값 바꾸기");


        // 5. textarea 내에 태그를 포함한 문자열을 그대로 집어 넣기

        // text() 와의 차이점은 text는 태그를 포함하는 문자열을 그대로 넣는다.

        // ex) <b>안녕</b> 를 넣으면 textarea에서는 <b>안녕</b>가 출력됨

        // 하지만, 이 곳에서 사용할 html() 은 b태그가 적용된 안녕이 출력되게 됨

        $("textarea[name=txtarea]").html("<b>안녕</b>");


        // 6. 태그의 속성값을 가져오기

        // 아래처럼하면, style이라는 속성의 값을 가져옴

        var styleValue = $("input[name=second]").attr("style");

        // a 태그의 href 속성값을 가져옴

        var hrefVal = $("#aTag").attr("href");


        // 7. 태그의 속성값을 바까보자.

        // name이 second인 input 태그의 style 속성을 바까 글자색을 노랑색으로 바꿈

        $("input[name=second]").attr("style","color:yellow;");

        // a태그의 href 속성값을 naver로 바꿈

        $("#aTag").attr("href","http://www.naver.com");


        // 8. 태그 내에 추가하기

        // 위의 html()이나 text()는 태그 사이에 지정한 걸로 완전히 치완되버리는 것!

        // 하지만, append는 기존에 있던 것에다가 추가되는 개념이다.

        // 예를들어, 지금 table태그 내에 최초에 <tr><td>안녕</td></tr> 이 있는데

        // $("#tableTag").html("<tr><td>추가</td></tr>"); 을 하면 기존의 안녕은 없어지고

        // 추가가 들어갈 것이지만 append를 하면 안녕 바로 아래에 추가가 붙게된다.

        $("#tableTag").append("<tr><td>추가</td></tr>");


        // 9. 위의 append()가 지정한 태그 내부에 하나씩 아래로 붙는다면

        // prepend()는 지정한 태그 내부에서 가장 마지막에 삽입된 녀석의 앞에 붙게 됩니다.

        // 예를들어, table 태그 내에 안녕과 추가가 들어 있는데 prepend로 추가2를 넣으면

        // 추가 뒤에 들어오는게 아니고 안녕 앞에 추가2가 들어오게 됩니다.

        $("#tableTag").prepend("<tr><td>추가2</td></tr>");


        // 10. 지정한 태그 다음 위치에 추가하기

        $("#tableTag").after("<p>table태그 다음에 추가합니다.</p>");


        // 11. 지정한 태그 이전 위치에 추가하기

        $("#tableTag").before("<span>table태그 이전에 추가합니다.</span>");


  // 12. 태그 내부의 지정한 것만 비우기

  $("#tableTag").remove(".hello");


        // 13. 지정한 태그 내부 전부를 비우기

        $("#tableTag").empty(); // 기존에 내부에 있던 tr td 등을 싹 비워버립니다.

      });

    </script>

  </body>

</html>



[    Javascript의 브라우저 창 Window 객체(부모창 자식창 값 주고 받기 포함)    ]



window 객체


    • Javascript 실행시 가장 상위에 존재하는 객체

        • 변수를 선언하거나 함수 선언시 window 객체안에 선언됨
        • 크롬에서 F12를 눌러 console탭에서 window 객체를 입력해서 다양한 속성들을 조회해볼 수 있음.
    • 표시된 웹 페이지의 정보에 접근하거나 변경을 할 수 있음

        • window.location : 현재 브라우저 창의 주소를 나타내는 객체
        • window.location.href : 브라우저 창에 입력된 주소, 변경 가능
          • ex) window.location.href = "이동하고자하는 url "
          • ex) window.location.replace = "이동하고자하는 url" 위의 것과 차이점은 이동하고나서 뒤로가기 버튼이 활성화되지 않음.
        • window.open : 새창 띄우기
        • window.navigator : 브라우저 자체에 대한 정보
        • window.screen : 디스플레이의 사이즈
        • window.document : 웹 페이지 문서의 HTML, CSS 등에 대한 접근 가능









    • 2. [ window 객체를 이용해 새창을 띄우고 부모창과 자식창 사이에서 값을 주고 받는 방법 ]


    • var windowObj = window.open("새창에서띄울url","창이름","창의설정(옵션값)"); 처럼하면 현재창에서 새로운 창이 뜨게 됩니다. 이때 기존의 창이 부모창이 되고 새로 뜨게된 창이 자식창이되게되는데

      이때, 부모창과 자식창 사이에서 값을 주고 받기 위해서는 띄운 창의 window 객체를 저장할 변수를 선언해 저장해 놔야합니다.(windowObj)

      그런 다음 부모 창에서는 이 windowObj 변수를 이용해 자식창에 값을 전달할 수 있습니다. 예를들어, 

      windowObj.document.text2.value = "값 입력"; 이런식으로 자식창(새창)의 document 객체에 얼마든지 접근할 수 있습니다. 

      그러면, 반대로 자식창에서 부모창으로 값을 보내기 위해서는 어떤 식으로 해야할까요??

      일단, 부모창에서 자식창을 생성하면 이 자식창은 생성과 동시에 opener 라는 객체를 가지게 되고 이 opener는 바로 부모객체의 window 객체를 가리키게 됩니다. 따라서

      opener.document.parentText.value = document.text2.value; 이런식으로 자식에 있는 값을 부모 창으로 넘기는게 가능합니다.


      먼저, windowOpen1.html 즉, 부모창 예제 코드입니다.




다음으로 windowOpen2.html 즉, 자식창 예제 코드입니다.



위의 예제는 동일 폴더 내에서 테스트한걸로 크롬 보안상 작동하지 않을 수 있지만, 웹 서버에서 할 때는 잘 작동한다는 점 참고해주시기 바랍니다.



[    테이블 수정하기 Alter Table    ]




테이블을 생성하고... DROP하는건 다 하실 수 있다는 전제하에 Alter table에 관해 포스팅을 진행하겠습니다.



1. 테이블에 새로운 컬럼(필드)를 추가하기


ex)

Alter Table 테이블명 ADD name varchar2(10) Not null




2. 테이블의 특정 필드의 데이터 타입을 변경하기


ex)

Alter Table 테이블명 MODIFY name varchar2(30)




3. 테이블의 특정 컬럼을 삭제하기


ex)

Alter Table 테이블명 DROP COLUMN 컬럼명;




4. 테이블의 컬럼명 변경하기


ex)

Alter Table 테이블명 RENAME COLUMN 변경전컬럼명 TO 변경할컬럼명;





5. 테이블의 특정 컬럼에 제약조건 추가하기


ex)

Alter Table 테이블명 ADD CONSTRAINTS 제약조건별칭 Primary key (col3);

Alter Table 테이블명 ADD CONSTRAINTS 제약조건별칭 Foreign key (bno) references 참조할테이블(참조할컬럼);





6. 테이블의 특정 컬럼에 걸려있는 제약조건 삭제하기


ex)

Alter Table 테이블명 DROP CONSTRAINTS 제약조건별칭;


참고)

제약조건 별칭은

select * from user_constraints where table_name='테이블명' 으로 조회해서 제약조건 별칭을 알 수 있습니다.

[    Oracle DB 대표적으로 많이 쓰이는 데이터 타입 정리    ]



1. 문자


-    char(n)

: n Byte 고정 크기

-    varchar2(n)

: 최대 n Byte 크기지만 사용한 Byte에 맞게끔 저장됨 


ex) varchar2(20) 으로 20Byte로 생성했는데, "남자" 라고 2글자 4Byte만 저장했다면 4Byte로 저장되게 됨.


될 수 있으면 char보단 varchar2 사용이 권장된다.


참고)

Mysql에서는 varchar2가 아닌 => varchar(n) 을 사용하고, 긴 문자열을 다룰 때 text 라는 데이터 타입도 자주 사용됩니다.


2. 숫자

-    Number(p,s) 

숫자에서 가장 대표적으로 사용되는 데이터 타입입니다. 정수, 실수 다 사용 가능하며 p는 유효 숫자 개수, s는 소수점 이하 자리수를 가리킵니다.

ex) Number(5,1) 로 지정하고 데이터 234.15를 insert하면 5개 숫자가 유효하고 소수 첫번째 자리까지 표현함으로 반올림되어 234.2가 저장됩니다. 

만약, Number(3.1)로 해놧는데 234.15를 저장하면 소수점 이하 첫째 자리까지해서 234.2가 되더라도 총 4개의 숫자를 저장해야하는데 p를 3으로 입력했기 때문에

오류가 발생합니다.

p와 s는 생략 가능합니다.


참고)

Mysql에서는 Number 속성이 없습니다.

숫자를 사용할 때는

- int(n)

- bigInt(n)

- float 

등을 사용합니다. 이때 주의할 점은 bigInt의 경우 자바에서 String 타입으로 받아야 한다는 점입니다.!!


3. 날짜

-    Date

: 연/월/일 시/분/ 까지 표현할 수 있는 많이 사용되는 날짜 타입입니다.

default 값으로 현재 날짜를 입력하고자 할 때는 sysDate를 사용해 입력합니다.

ex) insert into table명(regdate) values( sysdate );

-    Timestamp

: Date가 시/분/초 까지만 표현 가능하다면 timestamp는 연/월/일 시/분/초 + ms(밀리세컨드) 까지 더 정밀하게 표현할 수 있습니다.

default 값으로 현재 날짜를 입력하고자 할 때는 systimestamp를 사용합니다.

ex) insert into table명(컬럼명) values( systimestamp );


참고)

Mysql에서는 now()를 이용해서 현재 시간을 입력할 수 있습니다.


4. NULL

: 값이 없음을 의미하는 데이터 타입입니다. 보통 테이블 생성시 컬럼에 별도로 설정하지 않으면 Null값을 허용하도록 default로 만들어지게 됩니다.

 따라서 insert시 컬럼에 값을 입력하지 않으면 Null이 들어가게 됩니다. 하지만

Null을 허용하고 싶지 않다면

제한자 객체인 "Not Null"을 사용하면 됩니다.

ex) regdate Not Null default systimestamp;


5. 대용량 데이터(LOB : Large Object Block)

: 대용량 데이터를 담기 위해 사용되는 타입입니다.

- 문자 : => CBLOB 을 사용합니다.

- 그 외(이미지,동영상 등) => BLOB을 사용합니다.


[    스프링과 안드로이드 연동5 : Javascript에서 Android 함수를 호출하기    ]



요즘은 웹을 개발하고 웹뷰를 이용해서 안드로이드에 붙이는 식으로 해서 반응형으로 하이브리드 앱을 만드는 경우가 많은데,


이러한 경우 웹뷰에서 버튼을 클릭한다거나 했을 때 자바스크립트에서 안드로이드에 있는 함수를 호출해서 


안드로이드를 제어하고 싶은 경우가 있다.


----------------------------------------------------------------------------------------------------------------------------------


먼저, 웹뷰를 연결해 세팅부터 하자.


1. [    WebView(웹뷰) 세팅    ]


일단 스프링 웹 프로젝트에서 작성한 웹 페이지를 띄우는 웹뷰를 작성한다.

그 다음에 


2. 자바스크립트에서 호출시 수행할 안드로이드 메서드를 작성한다.


이때, 자바스크립트와 안드로이드를 중간에서 인터페이스 역할을 할 클래스를 작성해서 그 내부에

메서드를 정의하도록 한다.


당연히 인터넷 작업을 해야함으로

<!-- 인터넷 접속 권한 추가 -->
<uses-permission android:name="android.permission.INTERNET" />

를 manifest에 추가해주어야 하고

네트워크 작업은 백그라운드 쓰레드로 해야하며


백그라운드 쓰레드에서는 메인 뷰의 화면 제어를 할 수 없음으로

handler에게 대신해달라고 요청을 해야한다.


앞에선 계속 그렇게 해왔는데 이 2가지를 한번에 하는 것이


handler.post(new Runnable(){ run() }) 을 이용한 방식이다.


이런식으로해서 JavascriptInterface 클래스를 만들도록 한다.





3. 만들어준 JavascriptInterface 클래스를 웹뷰에 등록해 주어야 한다.



이때 중요한 점은 

webView1.addJavascriptInterface(new JavascriptInterface(),"myJSInterfaceName");

에서 두번째 매개변수란에 myJSInterfaceName 처럼 인터페이스 이름을 지정하게 되어 있는데

이 이름을 이용해서 자바스크립트에서 호출하게 된다.



4. 스프링 웹 프로젝트의 자바스크립트에서 특정 이벤트 발생시 안드로이드 함수를 호출하는 구문을 작성하자.


가장 중요한 부분은 window.myJSInterfaceName.callMethodName(str); 부분이다.

window.(지정한 javascript인터페이스명).수행할메서드명() 으로 호출하게 된다.

function callAndroid(){
    var str = document.getElementById("txtName").value;
    window.myJSInterfaceName.callMethodName(str);
}

<form id="formName" action="">
    <input id="txtName" type="text" />
    <button onclick="javascript:callAndroid()">호출하기</button>
 
</form>


여기까지 했다면, 


웹뷰 상에서 안드로이드 사용자가 "호출하기" 버튼을 클릭시에 callAndroid() 자바스크립트 메서드가 수행되고


해당 자바스크립트 메서드에서 window.myJSInterfaceName.callMethodName(str); 을 통해 안드로이드 메서드를 

호출하여, TextView에 있는 메시지를 웹뷰를 통해 입력한 값으로 세팅하게 된다.



안드로이드 쪽 전체 코드는 다음과 같다.



[    스프링과 안드로이드 연동4 : JSON으로 가져오기    ]



이번에는 안드로이드에서 스프링 프로젝트로 요청시 JSON으로 서버에서 제공하는 데이터를 가져오는 방식을 다루어 보겠습니다.


1. 먼저, 스프링 프로젝트에서 pom.xml에 라이브러리를 추가합니다.


1) jackson-databind 추가

: @ResponseBody로 반환시 필요

<dependency>

<groupId>com.fasterxml.jackson.core</groupId>

<artifactId>jackson-databind</artifactId>

<version>2.8.7</version>

   </dependency>


2) jason-simple 추가

: JSONObject와 같은 json 객체 생성시 필요

<dependency>

<groupId>com.googlecode.json-simple</groupId>

<artifactId>json-simple</artifactId>

<version>1.1.1</version>

  </dependency>


2. 다음으로 스프링 컨트롤러 부분을 작성합니다. 스프링 컨트롤러에서 다음의 코드를 입력합니다.



http://localhost:8080/json.do 로 요청시 필요한 데이터를 JSONObject에 저장시켜 반환하는 컨트롤러 부분입니다.

반환시 한글 인코딩 문제 때문에 produces 부분에서 JSON 객체 생성시 utf-8로 인코딩하겠다는 설정을 하고,

JSONObject는 {변수명:값} 을 담을 수 있음으로

JSONArray로 배열 객체를 생성해 여기에 JSONObject 객체를 담아서

sendData라는 변수명으로 이 배열을 저장시켜 JSON으로 반환하게 됩니다.




2. 안드로이드에서 작업


안드로이드에서도 JSON 객체를 받아오기 위해서 2개의 라이브러리를 추가해주어야 합니다.

1. httpclient-4.2.2.jar

2. httpcore-4.2.2.jar

두개의 라이브러리를 mvnrepository 에서 검색해서 files 부분에 보면 다운로드를 할 수 있는데 다운로드 합니다.

그리고 안드로이드 프로젝트에서 프로젝트 보기가 Android로 되어 있는데 이 부분을 눌러 Project로 변경합니다.

그러면, App/libs 폴더가 보일겁니다.

여기 Android일 땐 보이지 않지만

Project로 변경시 app/libs 폴더가 보입니다. 그러면 이 폴더에 다운받은 2개의 라이브러리를 집어 넣습니다.


다음으로, android 형식으로 보기를 다시 누르고, Gradle Scripts 의 build.gradle 부분에 들어가서


dependency 부분에 


다음의 2줄을 추가해줍니다.

compile files('libs/httpclient-4.2.2.jar')
compile files('libs/httpcore-4.2.2.jar')


그리고 Sync Now 선택해주면 적용이 된 것입니다.



3. 이제 안드로이드에서 코드를 작성합니다.


역시 네트워크 작업임으로 이전에서 했던 3가지 사항을 준수합니다.


1) 인터넷 사용 권한 설정(manifest에)

<!-- 인터넷 접속 권한 추가 -->
<uses-permission android:name="android.permission.INTERNET" />


2) 네트워크 작업임으로 백그라운드 스레드에서 작업할 것


3) 백그라운드 스레드에선 메인 뷰에 접근할 수 없음으로 핸들러를 통해 뷰에 접근할 것!






**** 마지막으로.........

위의 경우에서는 안드로이드에서 스프링 컨트롤러로 post로 요청을 보낼 때 데이터를 같이 보내지 않았다. 만약 데이터를 같이 넘겨줘야 하는 경우에는

// NameValuePair : 변수명과 값을 함께 저장하는 객체로 제공되는 객체이다.
ArrayList<NameValuePair> postData = new ArrayList<>();
// post 방식으로 전달할 값들을 postData 객체에 집어 넣는다.
postData.add(new BasicNameValuePair("id","아이디"));
postData.add(new BasicNameValuePair("pw","패스워드"));
// url encoding이 필요한 값들(한글, 특수문자) : 한글은 인코딩안해주면 깨짐으로 인코딩을 한다.
UrlEncodedFormEntity request = new UrlEncodedFormEntity(postData,"utf-8");
HttpPost httpPost = new HttpPost(url);
// post 방식으로 전달할 데이터 설정
httpPost.setEntity(request);
// post 방식으로 전송, 응답결과는 response로 넘어옴
HttpResponse response = http.execute(httpPost);
// response text를 스트링으로 변환
String body = EntityUtils.toString(response.getEntity());
// 스트링을 json으로 변환한다.
JSONObject obj = new JSONObject(body);

// 스프링 컨트롤러에서 리턴해줄 때 저장했던 값을 꺼냄
String message = obj.getString("message");

과 같이 작성하면 된다.



[  스프링과 안드로이드 연동3 : 서버에서 XML로 반환해 안드로이드로 가져오기 ]


먼저, 이 작업을 하기 위해서 Spring 프로젝트에서 pom.xml에 3가지 라이브러리를 추가해주어야 한다.

[필요 라이브러리]


1.org.jdom을 mvnrepoisitory에서 검색해 dependency를 pom.xml에 추가한다.

-> 자바 객체를 가지고 xml을 생성해주는 라이브러리


2.json-simple 도 추가

-> json 객체를 생성


3.jackson-databind 추가

-> 스프링 컨트롤러에서 @ResponseBody로 json으로 전송하기 위해 사용



3개의 라이브러리를 pom.xml에 추가했으면 다음으로 스프링 컨트롤러에서


XML로 보낼 데이터를 구성해 전달하는 코드를 작성해 주어야 한다.

(mvnrepository에서 검색해서 추가하자.)


그 다음 아래는 서버쪽 컨트롤러 코드에 대한 부분이다.


bookService.bookList()를 통해서 db에서 BookDTO 객체를 갖는 List를 반환받아서 이 List 정보를 XML 형태로 만들어서

return하는 내용의 코드이다. XML을 구성하는 부분은 주석으로 남겨 놓았다.


List<BookDTO> 를 가지고 구성할 XML 의 구성은 아래와 같다.


<books>
<book>
<book_code>1</book_code>
<book_name>자바</book_name>
<press>출판사</press>
</book>
</books>




여기까지 했으면 스프링 프로젝트에서 필요한 부분은 끝이났다.

이제 안드로이드에서 스프링에서 던진 데이터를 저장하기 위한 BookDTO 클래스를 만들자




그리고 마지막으로 안드로이드에 관련된 작업이다.

역시 네트워크 작업임으로 앞에서 한 예제에서 주의해야하는 3가지 사항을 지켜주어야 한다.

1. 네트워크 작업임으로 백그라운드 스레드로 동작시켜야한다.

2. 네트워크 접근이 필요함으로 인터넷 권한을 주어야한다.

3. 백그라운드 스레드에서 메인 뷰에 접근할 수 없음으로 핸들러를 통해 처리해야한다.


위 사항을 기반으로해서 XMLPullparser를 이용해 데이터를 끄집어 내 핸들러를 통해 안드로이드에 출력해주면 된다.


[    스프링과 안드로이드 연동2 : 서버에서 안드로이드로 이미지 가져오기    ]





이전 포스팅과 마찬가지로 네트워크를 통한 작업임으로 주의해야할 사항이 2가지 있다.



1. 안드로이드는 네트워크 작업시 "백그라운드 스레드"로 동작을 시켜야한다.(대용량 데이터를 네트워크로 가져오는 동안 다른 작업은 할 수 없게 될 수 있음으로...)

2. 백그라운드 스레드에서는 메인 뷰 화면을 제어할 수 없다. 따라서 가져온 이미지를 백그라운드 스레드에서 적용할 수가 

없다. 따라서, 핸들러에게 대신 요청을 해주어야 한다.

3. 인터넷을 사용해야 함으로 manifest에 인터넷 사용 권한을 추가해 주어야 한다.



라는 사항을 유의하고 작업하면 그 외의 부분은 이전과 거의 유사하다.

나머지는 코드의 주석을 통해 설명하겠습니다.



[    float 속성    ]


float 속성은 본래 이미지 태그와 글단락이 같이 있을 때 한 줄에 같이 나오기 위해 주로 사용되었는데


그뿐만 아니라 레이아웃을 잡을때도 많이 이용되게 되었다. 하지만 최근에는 flex가 도입되면서 점차 float를 이용한

레이아웃을 잡는 방식은 줄어들고 있는 추세이다.


float:left

float:right

처럼 사용해서 태그가 배치될 위치를 지정하게 된다.

아래의 예제에서는 본래 이미지 태그와 p태그는 block 속성에 의해 이미지 태그가 오고 그 아래에 문단 태그가 와야 정상이지만

이미지에 float:right를 줌으로서 img는 오른쪽으로 배치가 되고 문단이 왼쪽으로 배치되었다.


하지만 그 이후에 또 나오는 p태그(id="clearAdjustP")의 경우는 이러한 float의 영향을 배제시키고 싶기 때문에

float를 해제하는 clear 속성을 이용해서 clear:both해주면 float:left든 float:right든 다 해제한다는 의미가 되기 때문에

정상적으로 이미지 태그 아래에 출력되게 되는 것을 알 수 있다.





float로 만든 Holy Grail Layout이다.

하지만... Flex가 더 유연함으로 FLex로 만드는 것을 권장한다...


[    반응형 웹을 만드는 Media Query    ]


화면 크기에 따라 반응하는 웹을 만드는데 필요한 media query에 대해 학습합니다.


@media (){

} 라는 구조를 가지고 있는대요


- @media(width:500px){

width가 500px일 때 지정할 속성을 지정하게 됨.

}


- @media(max-width:500px){

최대 500px까지 즉 0~500px까지를 의미

}


- @media(min-width:500px){

최소 500px 즉 500px 이상을 의미한다.

}


만약 500 ~ 600을 지정하고 싶다면?

cascading을 주의해야한다. 나중에 쓴 것이 우선순위가 더 높다는 것에 유의!

@media(max-width:600px){

background-color:red;

}

@media(max-width:500px){

background-color:blue;

}

이렇게하면 500 ~ 600 px까지는 red가 적용되고 500이하가 되면 blue가 적용된다.


만약 두개의 순서를 바꿔서 쓰면? 600px이하면 0픽셀이 될때까지 red가 나오게 된다. cascading에 의한 우선순위때문에

이점을 유의해서 작성하자.


또한, 모바일 기기에서도 반응형이 잘 적용될 수 있도록 다음의 meta 태그를 넣도록 하자.


 <meta name="viewport" content="width=device-width, initial-scale=1.0">



Flex로 만든 layout에 Media Query를 적용해 보기


아래는 모바일 화면(500px 보다 작아졌을 때) section 영역이 수직으로 main nav aside 순으로 보이고

기존에 nav와 aside가 가지고 있었던 border를 없애는걸 적용한 것이다.


따라서, section의 flex-direction:column으로 바꾸고 nav와 aside의 border를 border:none으로 한 다음에

flex의 order 속성을 이용해서 순서를 지정해주면 된다.


[    Flex    ]


기존의 table이나 div를 쓰고 position Float를 사용하던 방식에서 벗어나 Flex를 이용한 레이아웃을 구성하는 방법에 대해서알아봅시다.


1. flex 기본(display:flex, flex-direction, flex-basis) & flex-grow, flex-shrink 속성





2. 위에서 공부한 속성을 활용해서 대표적인 레이아웃 디자인인 Holy Grail Layout을 구성해보도록 하자.


이러한 구조를 만들어 볼 건데

먼저, (Header), (Nav, Main, AD), (Footer)로 구성되어 있다.

크게 태그로

1) header, section, footer 시멘틱 태그로 구성하고 section에는 nav, main, aside(광고부분) 시멘틱 태그를 배치한다.

<div class="container">

<header>헤더</header>

<section>

<nav></nav>

<main></main>

<aside></asdie>

</section>

<footer>푸터</footer>

</div>


2) 먼저 Flex를 사용하기 위해 가장 부모인 container에 플렉스를 사용하겠다고 display:flex를 지정한다.

그리고, 그 자식들은 수직으로 배치되어야 함으로 flex-direction:column을 지정해 준다.



3) 헌데 section 태그 내의 자식들(nav,main,aside)는 가로로 배치되어야한다.

따라서, 그들의 부모인 section에 flex를 사용하기 위해 display:flex를 지정하고, flex-direction:row를 지정한다.



4) 마지막으로, 화면을 줄여도 nav부분과 aside부분은 줄어들지 않기 위해서 flex-shrink를 지정할 건데

지정하지 않을 경우 flex-shrink:1 로 되어있다. 따라서 나머지 부분은 그대로 두고 

nav와 aside만 flex-shrink:0 을 지정해 창을 줄여도 그 크기는 줄어들지 않도록 설정하도록 한다.

또한 nav와 aside는 일정 크기를 가지도록 flex-basis:150px; 정도를 할당하도록 하자.


5) 기타사항


만약, section 태그 내에서 지금은 nav, main, aside 순으로 되어 있는데 이 순서를 main nav aside로 바꾸고 싶은 경우는 어떻게 할까


태그의 위치를 바꿀 수도 있지만 order라는 속성을 사용하면 된다.

main{

order:0;

}

nav{

order:1;

}

aside{

order:2;

}

로 지정하면 된다.


Ex)


[    position offset    ]



[엘리먼트의 위치를 정하는 속성 position 속성값 4가지]


1. static(기본값) : 움직이지 않고 정적인 상태 => position:static

2. relative : 부모 엘리먼트를 기준으로 상대적으로 움직인다 => position:relative


3. absolute : position값이 relative인 부모를 기준으로(없다면 웹페이지의 가장 가장자리 기준) 움직인다. => position:absolute
* 자식의 위치값이 absolute이면 부모와의 관계가 끊기고 그래서 자신의 크기가 딱 컨텐츠만 해진다.
그리고 값을 아예 없애면 원래 위치로 돌아간다.


4. fixed : 스크롤을 움직여도 지정된 위치에 고정된다. => position:fixed
* absolute와 마찬가지로 부모와의 관계가 끊기고 크기는 자신의 컨텐츠만 해진다.


offset

-    top 

-    right

-    bottom

-    left

~만큼씩 위치를 떨어져 위치될지 결정할 수 있는데 position 속성과 같이 사용된다.


아무것도 지정하지 않을 경우 position:static이 지정되어 있는데 이때는 이 offset 속성들은 영향을 주지 못한다.


따라서, position:relative 나 position:absolute를 사용해서 해야하는데


position:relative의 경우 부모와의 상대위치를 기준으로하는 것이다. 즉 자식이 position:relative라면 자신의 부모를 기준으로해서

left:10px; top:20px; 이면 부모를 기준으로해 위는 20px 떨어진 위치 왼쪽으로부터 10px 떨어진 위치에 위치하게 된다.


position:absolute의 경우 절대위치를 가리키는데 이떄는 position:static인 부모들은 무시하고 그 외의 position:relative나 position:absolute가 지정된 부모를 기준으로해서 배치되게 된다.


즉, 자신의 부모가 기준이 아니다!


또한 이러한 position:absolute는 z-index, fixed와 함께 사용되 메뉴를 구성하는데 사용될 수 있다.


position : fixed => 스크롤과 관계없이 고정된 위치에 배치되도록할 수 있다.(상단 메뉴등에 활용할 수 있음)



[    Layout 관련 속성    ]


- inline 요소
- block 요소

두 요소의 특징을 바꾸는 display 속성

display:inline 하면 block요소를 inline으로 바꿀 수 있고...
display:block 하면 inline요소를 block요소로 바꿀 수 있다.
display:none; 화면에서 아예 사라진다.(공간마저 잡지 않음)


padding : 내부 영역
vs
margin: 외부 영역


[    폰트 관련 속성 및 외부 폰트 설정하기    ]



1. 폰트 사이즈 속성 => font-size:5rem;

2. 텍스트 정렬 속성 =>text-align:justify;

left, right, justify 등을 지정할 수 있다. justify는 양옆을 딱딱 맞추는 정렬 방법으로 글자 사이 간격을 조정해 맞추게 된다.

3. 글씨체 속성 =>  font-family:"Times New Roman" Times, serif;

이렇게 3개를 써놓으면, 첫번째 걸 지정하되 해당 글씨체가 없으면 2번째 것 그것도 없으면 3번째 것을 지정하게 된다.     

 4. 폰트 bold 속성 => font-weight: bold;

 5. 줄 간격 속성 => line-height: 1.8; 

// 1.8 배로 지정


[    외부 폰트 임포트하고 사용하는 방법    ]

@font-face{

font-family:"GoodFont"; /*폰트 패밀리 이름 추가*/

src:url("resources/fonts/NANUMGOTHICCODING.TTF"); /*폰트 파일 주소*/

}

적용

body{

font-familly:"GoodFont" !important;

}



[    css의 가상 선택자    ]

1. a:link{
a태그의 기본 속성을 지정할 때
}

2. a:hover{
a태그에 마우스 오버시 속성을 지정할 때 사용
}

3. a:visited {
방문한 경우 속성을 지정
}

4. a:active{
누르는 순간을 지정
}

5. 태그:focus{
포커싱 되는 경우의 속성을 지정
}

이 태그의 경우는 input 태그 등이 focus를 얻을 시 하이라이트를 주는 등에 이용될 수 있음.


[    스프링(Spring)과 안드로이드(Android)를 연동해 서버 Html 소스 가져오기    ]


이번에는 스프링과 안드로이드를 연동해서 하는 예제 중 첫번째인 요청한 서버의 Html 소스코드를 가져오는 예제를 작성하겠습니다.


    1. 첫번째로 네트워크를 통해 가져와야함으로 "인터넷 권한을 설정해야 합니다."


manifest파일에 <!-- 인터넷 접속 권한 추가 -->

<uses-permission android:name="android.permission.INTERNET" /> 을 추가해줍니다.




2. 다음으로, 안드로이드 네트워크 작업시에 주의해야할 사항이 2가지 있습니다.



1) 안드로이드에서는 네트워크를 통해 작업을 할 때는 "반드시" "백그라운드 스레드"를 통해서 작업이 이루어져야합니다.


: 왜냐하면, 네트워크로 이미지 등을 받아올 때 시간이 다소 걸릴 수 있는데 메인스레드에서 작업할 시 다른 작업에도

지장을 주기 때문에 백그라운드에서 동작시키지 않을 경우 동작하지 않도록 제한이 걸려져 있습니다.


따라서, 쓰레드를 별도로 구현해 run() 메서드 내부에서 작업을 해야합니다.



2) 다음으로 알아둬야 할 사항은 "백그라운드 스레드"에서는 메인 스레드의 "뷰"를 제어할 수 없다는 것입니다.


예를들어, 백그라운드 스레드의 run()메서드 내에서 URL에 연결을 해 데이터를 가져오고 그 데이터를


이 run() 메서드 내부에서 textView같은 곳에 setText()해서 붙인다면 오류가 발생합니다. 이처럼 안드로이드는 백그라운드 스레드에서


메인스레드의 뷰에 접근할 수 없도록 제한이 걸려있습니다.


따라서 이러한 제한때문에 필요한 것이 "Handler"입니다.



"Handler란?"


: 핸들러는 안드로이드 네트워크 작업시 백그라운드에서 수행해야하는데 백그라운드 스레드에선 메인 뷰를 제어할 수 없기 때문에


백그라운드와 메인 스레드 사이의 통신(중계)를 담당하게 됩니다.


즉, 백그라운드 스레드는 핸들러에게 "네가 대신 화면 제어를 해줘"라고 sendMessage() 메서드를 통해 요청을 하게 됩니다.


그러면, 핸들러는 이 메시지를 받아 처리할 작업을 대신해주게 되는 것입니다.





3. 이제 이러한 이론적 지식을 가지고 아래의 구현 예제를 보겠습니다.



다음은 스프링 웹 서버로 요청해 해당 뷰의 html 소스를 가져오는 과정을 담은 예제입니다.


세부 부분에 대한 설명은 주석으로 남겨놓았습니다.



[    스프링에서 구글맵 연동하기    ]



스프링에서 구글맵을 연동하는 방법을 알아봅시다.

1. 먼저 구글 API 키를 다음 url로 들어가 얻습니다.

https://console.developers.google.com/flows/enableapi?apiid=maps_backend,geocoding_backend,directions_backend,distance_matrix_backend,elevation_backend,places_backend&reusekey=true


생성된 키는 곧 사용되니 잘 보관하고 계시고요


2. 두번째로 뷰단에서 html 코드를 작성합니다.

<div id="map"></div> 이 부분이 구글맵이 들어갈 자리고, style안에 #map 부분은 구글맵 컨테이너에 대한 스타일을 지정하는 부분입니다.





3. script 쪽을 작성합시다.

getAddr() 함수 부분은 제 코드임으로 구글맵과는 연관성이 없음으로 빼주시면 되고요


function initMap() 부분에서 지도의 초기화화 그려주는 역할을 하게 됩니다.

<script async defer  src="https://maps.googleapis.com/maps/api/js?key=자신의API키를넣으세요&callback=initMap">

</script>

부분은 자신의 API키를 넣어서 map을 로딩하는 요청을 보낼 수 있도록하고, 해당 로딩이 완료되면 callback에 지정한 

initMap() 메서드로 콜백이 들어와 지도를 그려주게 됩니다.


나머지 마크에 대한 설정, 위도 경도 세팅에 대한 내용은 코드 주석을 참고하세요.



+ Recent posts