안드로이드 개발 팁 #48 - 마크다운 텍스트를 HTML 텍스트로 변환하고 Jsoup Document 객체로 변환하는 방법
마크다운 텍스트를 HTML 텍스트로 변환하고 Jsoup Document 객체로 변환하는 방법
No. 48
23.10.05 (목) | Written by @dorian-mobileapp
시작하며…
스팀잇 포스트의 포맷은 마크다운 텍스트입니다. commonmark 라이브러리를 활용하여 이를 HTML 텍스트로 바꿀 수 있는데요. HTML의 일부 요소(element)들은 변경 또는 새로 추가해야 합니다. 각 요소를 Element 객체로 접근하는 방법을 Jsoup 라이브러리가 제공하고요. 오늘 포스트에서는 마크다운 텍스트를 HTML 텍스트로 변환 그리고 Jsoup Document 객체로 변환하는 방법에 대해 정리합니다.
필요 라이브러리
- jsoup
- build.gradle 파일의 dependencies 블록에 라이브러리 추가 코드 (2023. 10. 05 기준)
dependencies {
// ...
implementation 'org.jsoup:jsoup:1.16.1'
}
마크다운 텍스트를 HTML 텍스트로 변환하는 String 클래스의 확장 메소드
commonmark 라이브러리를 활용하여 변환된 HTML 텍스트는 Jsoup 클래스의 parse() 정적 메소드를 호출하여 Document 객체로 변환할 수 있습니다. 그런데 newline들이 화이트스페이스 문자로 변환되는 문제가 있었네요. 이것은 Document 클래스의 outputSettings(Document.OutputSettings().prettyPrint(false)) 메소드를 호출하여 해결하였습니다. (자세한 내용은 아래 코드의 링크 참조) 추가로 a 원소들의 href 속성은 링크로 연결될 URL들이지요. 이들이 URL 인코딩이 되지 않아 링크가 실행되지 않는 문제도 있었습니다. 이에 별도로 URL 인코딩을 추가 적용하였습니다.
import org.jsoup.Jsoup
import org.jsoup.nodes.Document
fun String.convertMarkdownToHtmlDocument(): Document {
val document = Jsoup.parse(convertMarkdownToHtml()).apply {
// to prevent changing newlines to whitespaces
// Refer to https://stackoverflow.com/questions/40396524/avoid-removal-of-spaces-and-newline-while-parsing-html-using-jsoup
outputSettings(Document.OutputSettings().prettyPrint(false))
select("a").forEach {
val hrefValue = it.attr("href")
it.attr("href", hrefValue.applyURLEncoding())
}
}
return document
}
- String.convertMarkdownToHtml() 메소드: 링크 참고
- String.applyURLEncoding() 메소드
fun String.applyURLEncoding(enc: String = "UTF-8"): String {
return try {
URLEncoder.encode(this, enc).replace("+", "%20")
}
catch (e: UnsupportedEncodingException) {
e.printStackTrace()
""
}
}
참고 링크
지난 안드로이드 개발 팁
- #47 - 마크다운 텍스트를 HTML 텍스트로 변환 방법
- #46 - HTTPS URL이 유튜브 사이트인지 판별 방법
- #45 - HTTPS URL의 내용이 이미지인지 판별
- #44 - 유튜브 URL로부터 플레이 시작 위치 읽는 방법
- #43 - 유튜브 URL로부터 비디오 ID 읽는 방법
- #42 - 문자열이 여러 개의 서브문자열들 중 하나로 시작하는 또는 끝나는지 판별하기
- #41 - 액티비티에 전체 화면 적용 방법 (린백 옵션)
- #40 - 웹뷰에서 구글 로그인 우회 방법
- #39 - 스크린 항상 키는 방법
- #38 - 핸들러를 코루틴으로 대체하기
- #37 - 내비게이션 활용하여 프래그먼트간 이동
- #36 - DB Browser for SQLite에서 쿼리 실행 방법
- #35 - DB Browser for SQLite 사용법
- #34 - SQLite 데이터베이스 도구 DB Browser for SQLite 다운로드
- #33 - adb 명령어 사용을 위한 PATH 환경 변수 설정
- #32 - 코틀린 언어로 구현한 확장 함수를 자바 코드에서 호출하는 방법
- #31 - UTC 시간을 지역 시간으로 변환하는 함수
- #30 - Collection, MutableCollection 연산 확장 함수 filter, count, indexOfFirst, sortWith, sortedWith
- #29 - 프로젝트에 벡터 이미지 추가하기
- #28 - JSON 자료를 데이터 클래스로 자동 변환하는 방법
- #27 - 페이스북 로그인이 안 되는 문제
- #26 - 레이아웃 XML 파일에 이모지 문자 넣는 방법
- #25 - TED permission 라이브러리를 활용한 권한 요청
- #24 - EditText 뷰에 텍스트 입력하고 0.5초 후 액션 설정
- #23 - Uri 객체로부터 읽은 파라메터에서 '+' 문자가 ' '로 바뀌어 있는 문제
- #22 - RxJava 활용하여 몇초 후 코드 실행
- #21 - 맥북 아이클라우드로 프로젝트 복사 후 빌드 안 되는 문제
- #20 - 툴바 정의 방법 (1) 타이틀 및 배경색 설정
- #19 - Index corrupted 오류
- #18 - 오래된 프로젝트의 build.gradle 파일 수정
- #17 - 뷰 바인딩 적용된 프래그먼트에 데이터 바인딩 적용 후 빌드시 발생하는 오류
- #16 - 특정 일이 속하는 주의 모든 날짜를 배열로 구하는 방법
- #15 - RecyclerView에 리스트를 로딩한 후 처리할 일 작성
- #14 - RecyclerView 뷰에서 항목 클릭시 뷰가 깜빡이는 문제
- #13 - 공통으로 사용할 색상 리소스 만들고 뷰에 적용
- #12 - 코틀린 언어 변환시 추가로 수정할 build.gradle 파일들
- #11 - 리스트/배열로부터 찾을 원소의 위치 읽기
- #10 - 앱의 다크 모드 진입 막는 방법
- #9 - 데이터 바인딩/뷰 바인딩 사용하지 않을 경우 자체적으로 만드는 Views 클래스
- #8 - TextView에 linear gradient color 적용하기
- #7 - 다이얼 화면 연결하기 위해 Activity 클래스의 확장 메소드 작성
- #6 - 웹 브라우저를 여는 확장 메소드 작성
- #5 - HTML 적용된 TextView에서 링크 클릭은 어떻게 구현?
- #4 - RxJava의 Observable, Single 객체의 기본 설정 수행 메소드 정의하기
- #3 - 특정 화면 이동시 다른 화면 모두 닫기
- #2 - HTML 이스케이핑 적용된 문자 풀어주기
- #1 - TextView로 HTML 내용 보여주기
Layout provided by Steemit Enhancer hommage by ayogom
Posted through the ECblog app (https://blog.etain.club)
안녕하세요.
SteemitKorea팀에서 제공하는 'steemit-enhancer'를 사용해 주셔서 감사합니다. 개선 사항이 있으면 언제나 저에게 연락을 주시면 되고, 관심이 있으신 분들은 https://cafe.naver.com/steemitkorea/425 에서 받아보실 수 있습니다. 사용시 @응원해 가 포함이 되며, 악용시에는 모든 서비스에서 제외될 수 있음을 알려드립니다.
[광고] STEEM 개발자 커뮤니티에 참여 하시면, 다양한 혜택을 받을 수 있습니다.
Thank you, friend!


I'm @steem.history, who is steem witness.
Thank you for witnessvoting for me.
please click it!
(Go to https://steemit.com/~witnesses and type fbslo at the bottom of the page)
The weight is reduced because of the lack of Voting Power. If you vote for me as a witness, you can get my little vote.
안녕하세요.
이 글은 SteemitKorea팀(@ayogom)님께서 저자이신 @dorian-mobileapp님을 응원하는 글입니다.
소정의 보팅을 해드렸습니다 ^^ 항상 좋은글 부탁드립니다
SteemitKorea팀에서는 보다 즐거운 steemit 생활을 위해 노력하고 있습니다.
이 글은 다음날 다시 한번 포스팅을 통해 소개 될 예정입니다. 감사합니다!
Upvoted! Thank you for supporting witness @jswit.
이제 대충 형태는 파악할 수 있는 정도가 되었습니다. 아직 제대로 이해하려면 시간이 많이 걸릴 것 같습니다. 계속 개발 글을 써주셔서 감사합니다.