지난 게시글 >>
Servlet 과 JSP를 이용한(모델2 형식) 블로그 만들기(12) - 회원정보 수정하기
섬머노트의 단점은 유튜브 파싱이 제대로 이루어지지 않고 그냥 생 유튜브 링크만 뜬다는 점이다.
이걸 iframe을 이용해 화면을 띄워보자.
이걸 하기 위해서 먼저 파싱(Parsing)이라는걸 알아보자. 파싱이라는건 문자열을 재조립해서 다시 합치는 과정이라고.. 하는데 다시 말하면 내가 원하는 정보를 빼내와서 내가 원하는 형태로 조립하는 형태로 봐도 된다.
자, 그럼 이걸 왜 하냐면, 문서를 받아와서 HTML 태그로 아예 만들어버릴 예정이기 때문이다.
데이터베이스에 들어가는 형태가 다음과 같이.
다음처럼 a태그를 합해서 들어가기 때문에 이 값을 그대로 iframe에 넣으면 값을 읽지 못해서 유튜브 영상을 띄울수가 없다.
그래서 태그 내에서 값을 추출하는 파싱 형태를 취한다.
java에서 사용하는 대표적인 라이브러리는 jsoup인데, jsoup는 단순히 코드를 파싱해주는 것만 아니라 request, response 값을 이동시켜주는 역할도 한다.
역시 이것도 maven responsitory에서 다운받자.
이걸 다운 받고..
lib 폴더에 넣어줍시다.
이제 여기서 흐르는 값들을 html로 파싱할 수 있게 되었습니다.
파싱하는 코드를 보자.
- YoutubeParser.java
package com.cos.blog.util;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.jsoup.select.Elements;
public class YoutubeParser {
public static String getYoutubePreview(String content) {
String another = null;
String youtubeurl = null;
Document doc = Jsoup.parse(content);
// System.out.println(doc);
Elements aTags = doc.select("a");
// System.out.println(aTags);
//System.out.println(value);
for (Element aTag : aTags) {
String value = aTag.attr("href");
System.out.println("aTag : "+aTag);
System.out.println("value : " + value);
if (value.contains("https://www.youtube.com/") && !aTag.attr("target").equals("_blank")) {
System.out.println("aTags: " + aTags);
System.out.println("value: " + value);
String[] sp = value.split("=");
another = sp[1];
//System.out.println("another :" +another);
youtubeurl = "</br><iframe src='https://www.youtube.com/embed/" + another
+ "' style='width: 500px; height: 300px' allowfullscreen></iframe></br>";
aTag.after(youtubeurl);
} else if (value.contains("https://youtu.be/")) {
String[] sp = value.split("/");
another = sp[3];
//system.out.println("another :"+another);
youtubeurl = "</br><iframe src='https://www.youtube.com/embed/" + another
+ "' style='width: 500px; height: 300px' allowfullscreen></iframe></br>";
aTag.after(youtubeurl);
}
}
// System.out.println(doc);
return doc.toString();
}
}
먼저 html 전체로 값을 파싱한다. 받아오는 board 내 content 가 String의 형태이기 때문에 html 태그의 형태로 파싱해준다.
select 함수를 이용해 a태그만 가진 태그만 가져온다. 태그를 선택할 때는 태그 이름(ex. a,p, img...)만 입력하고, id값을 찾아야 할때는 앞에 #만 붙이면 된다.
이제 a태그 내의 값들을 가져올 것이다. 일단, a태그가 두 개 이상일수도 있으니 이를 foreach문을 이용해서 모든 a태그를 찾도록 한다.
foreach 구문에서 a태그 내에서 진짜 우리가 필요한 값인 주소를 가져올 것이다.
두 가지 방법이 있는데, attr과 text가 있다.
attr의 경우 태그 내의 속성값을 가져오고, text의 경우 a태그 사이의 텍스트 값을 가져온다.
웬만하면 text보단 attr을 추천하는데, text는 하이퍼링크 내부의 내용이 바뀌게 되면 영상을 출력하지 못하기 때문이다.
물론 저도 attr을 이용했고요.
이제 영상의 id값만 따올 것이다. 그런데 유튜브는 링크가 두 가지이다.
이 링크의 id값은 v=(value)값이고 ,
이 링크의 id값은 / 뒤의 값이다.
그렇기 때문에 이 링크를 둘 다 인식할 수 있도록 if문으로 분기점을 만들어준다. 뒤에 아이디를 추려올 수 있는 부분을 split으로 잘라서 가져온다. 그 값을 변수로 iframe 소스로 만들어서, 아이디 값을 변수로 넣는다.
유튜브 영상을 iframe에 넣을때는 꼭 https://www.youtube.com/ 뒤에 embed를 붙여야만 영상이 뜨니까 꼭.. 꼭 쓰시기
이후에 그것을 a 태그 뒤에 after 함수 뒤에 붙인다. after로 붙인 값은 doc에도 그대로 적용 잘 되기 때문에 그걸 다시 string 으로 변환해서 값을 리턴받는다. 이 리턴받은 값을 세션에 response의 속성에 담고 이동하면 끝~
이 일은 상세보기에서 이루어지니까, 상세보기의 응답을 담당하는 detailAction을 수정하면 되겠다.
- BoardDetailAction.java
package com.cos.blog.action.board;
import java.io.IOException;
import javax.servlet.RequestDispatcher;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import com.cos.blog.action.Action;
import com.cos.blog.dto.DetailResponseDto;
import com.cos.blog.model.Board;
import com.cos.blog.repository.BoardRepository;
import com.cos.blog.util.Script;
import com.cos.blog.util.YoutubeParser;
public class BoardDetailAction implements Action{
private String result=null;
@Override
public void execute(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
if(
@@ -29,6 +33,11 @@ public void execute(HttpServletRequest request, HttpServletResponse response) th
DetailResponseDto dto = boardRepository.findById(id);
if(dto!=null) {
Board board=dto.getBoard();
result=YoutubeParser.getYoutubePreview(board.getContent());
board.setContent(result);
request.setAttribute("dto", dto);
//request를 유지하기 때문에 데이터를 담고 이동할때 사용
RequestDispatcher dis=request.getRequestDispatcher("board/detail.jsp");
dis.forward(request, response);
}else {
Script.back("잘못된 접근입니다.", response);
}
}
}
- 결과
ps 1. iframe만 담긴 변수만 가져가도 영상의 프레임은 뜬다. 그러나, url과 함께 적은 기타 글들이 보이지 않게 되므로, doc(전체 html)을 문자열로 재파싱해서 리턴하는 이유이다.
ps 2. 이게 섬머노트 오류인지 모르겠는데...
두 개 이상 같은 영상을 붙이게 되면 자동적으로 하나가 target 속성으로 걸리면서 두 개가 출력되는 버그가 있다.
분기를 단순히 주소로만 두고,
for (Element aTag : aTags) {
String value = aTag.attr("href");
//System.out.println("aTag : "+aTag);
//System.out.println("value : " + value);
if (value.contains("https://www.youtube.com/")) {
System.out.println("aTags: " + aTags);
System.out.println("value: " + value);
String[] sp = value.split("=");
another = sp[1];
System.out.println("another :" +another);
youtubeurl = "</br><iframe src='https://www.youtube.com/embed/" + another
+ "' style='width: 500px; height: 300px' allowfullscreen></iframe></br>";
aTag.after(youtubeurl);
} else if (value.contains("https://youtu.be/")) {
String[] sp = value.split("/");
another = sp[3];
//system.out.println("another :"+another);
youtubeurl = "</br><iframe src='https://www.youtube.com/embed/" + another
+ "' style='width: 500px; height: 300px' allowfullscreen></iframe></br>";
aTag.after(youtubeurl);
}
이 상태로 그대로 영상을 iframe으로 출력하면 한 url에 두개가 있다고 인식하기 때문에 반드시 url쪽에 target=blank 속성이 아닐때 출력하도록 if문을 둬야 한다 (위에 있는 코드가 수정된 코드이다.)
(오류를 고치지 않으면 이런 참사가 일어납니다)