Translate
2015년 4월 29일 수요일
[IntelliJ][SVN] 특정 Revision 으로 되돌리는 방법
IntelliJ 를 사용한지 1년이 넘었는데 그동안 이전의 Revision 으로 소스를 되돌려본적이 없었던 터라..
아주 간단한 작업인데 꽤 고생을 했다.
방법은 다음과 같다.
1. 되돌릴 디렉토리 및 파일을 선택 후 오른쪽 버튼 클릭 => Subversion => Update File
2. Update/Switch to specific revision 체크 => ... 체크 => 되돌릴 Revision 항목 체크 => OK
3. 선택한 Revision Number 로 표시되는 것을 확인 => OK
또 다른 방법은 다음과 같다.
1. 되돌릴 파일을 선택 후 오른쪽 버튼 클릭 => Subversion => Show History
2. 원하는 Revision 을 선택 => Get
참조: https://www.jetbrains.com/idea/help/reverting-to-a-previous-version.html
PS.
두번째 방법 때문에 꽤 고생을 했다.
분명 링크대로 작업을 했는데 Get 항목이 비활성화 되어있어서 해당 Revision 으로 되돌 릴 수가 없었다.
한참동안 디렉토리의 History의 경우에는 Get 이 비활성화 되며 파일의 History 에서만 Get 이 활성화가 되었다.
링크의 내용을 다시 보니 "To revert a file to its previous version" 라는 걸 제대로 확인하지 않았었다.
2015년 4월 27일 월요일
[리뷰][서적] 손에 잡히는 정규표현식 - 출퇴근길 잠깐씩 읽어보며 정규식을 마스터해보자.
손에 잡히는 정규표현식 - 벤 포터 지음, 김경수 옮김/인사이트 |
전에 '손에 잡히는 Vim'(리뷰) 서적을 보고 '손에 잡히는' 시리즈가 상당히 마음에 들었다.
우연히 중고서점에 있는걸 발견하고 바로 구매를 했다.
책이 상당히 얇아서 출퇴근 시 짬짬히 읽을만 하다.
출근 시 20분 정도 책을 읽었던것 같은데 일주일도 안되어 다 읽었다.
지금까지 자주 사용하지 않던 기능들은 잊지 않기 위해 따로 정리를 해 두었다.
링크 - 정규표현식 고급기능 소개
정규식을 자주 사용하긴 했는데 알고나니 이렇게나 쉬운거였나?
싶을 정도로 양이 많지도 않고 아주 어려운 것도 아니다보니
정규표현식 책을 왜 이제서야 읽어봤을까 싶었다.
진작 책을 사서 한번 읽어봤으면 좋았을껄..
[Regex] 정규표현식 고급기능 소개
정규식표현식을 아주 잘하지는 못해도 어느정도 잘 쓰고 있다고 생각을 했는데 일반적인 책에 잠깐나온 정규식방법으로 공부하기도 했고.. 인터넷으로 그때그때 찾아서 하다보니 쉬운것만 알고있었지 지금까지는 제대로 사용을 하지 못했던 것 같다.
인터넷에서 많이들 소개하는 정규표현식 말고 좀 더 고급 기능과 응용을 정리하려 한다.
1. 수량자(Quantifier) - 탐욕적 수량자와 게으른 수량자
1004lucifer
정규식은 기본적으로 가능한 한 넓은 범위(탐욕적 수량자)로 매칭을 하게 되는데..
그 범위를 최소화 할 수 있는게 게으른수량자 방법이다.
탐욕적 수량자 게으른 수량자
* *?
+ +?
{n,} {n,}?
2. 역참조(backreferences)
1004lucifer
<(h\d)>.*?<\/\1>
<h1> 태그는 </h1> 태그로 끝나야 한다.
일반적인 방법으로는 각각의 상황에 대해서 정규식을 만들어야 하겠지만 역참조 방법을 이용하면 매칭된 문자열을 참조해서 간단하게 정규식이 가능하다.
사용방법은 괄호 "(, )" 로 묶은 공간을 \[숫자] 또는 $[숫자] 로 참조할 수 있다.
[숫자] 부분은 괄호 "(, )" 에서 매칭된 순서이다.
다음과 같다고 보면 된다.
Regex : \((\d*)\)(\d*)-(\d*)
String : (010)123-4567
\1 : 010
\2 : 123
\3 : 4567
\[숫자] 나 $[숫자] 를 사용하는 기준은 정규표현식 구현에 따라 다르다.
(자바스크립트의 경우에는 \[숫자] 를 사용한다.)
PS.
역참조는 꼭 찾기 기능에서만 사용할 수 있는게 아니다.
치환 기능에서도 역참조가 사용이 가능하다.
대부분의 에디터는 정규식 지원이 되는데 다음과 같이 문자열 치환이 가능하다.
'Replace All' 을 누르면 아래와 같이 치환이 된다.
3. 전후방탐색(lookaround)
- 전후방탐색 패턴은 일치영역을 발견해도 그 값을 반환하지 않는 패턴을 말한다.
기본 샘플
1) 전방탐색(lookahead)
- positive lookahead 사용방법
?= 사용 후 뒤에 일치할 패턴을 입력한다.
htt.*(?=:)
PS.
positive lookahead 를 이용한 문자열에서 단어 AND 연산이 가능하다.
링크 - [Regex] 정규표현식 AND 연산 사용방법
1004lucifer
- negative lookahead 사용방법
?! 사용 후 뒤에 일치하지 않을 패턴을 입력한다.
htt.*\/\/(?!eclipse)
2) 후방탐색(lookbehind)
(Javascript 에서는 지원되지 않는다.)
- positive lookbehind 사용방법
?<= 사용 후 뒤에 일치할 패턴을 입력한다.
positive 전방탐색에서 < 기호만 추가되었다.
(?<=\/\/).*\.
- negative lookbehind 사용방법
?<! 사용 후 뒤에 일치하지 않을 패턴을 입력한다.
negative 전방탐색에서 < 기호만 추가되었다.
(?<!https):.*
4. 조건
(Javascript 에서는 지원되지 않는다.)
- if문과 비슷한 느낌..
사용방법
(?(condition)true)
(?(condition)true|false)
다음과 같이 사용 시 - (?(condition)true)
\d{5}(?(?=-)-\d{4})
다음과 같이 사용 시 - (?(condition)true|false)
(\d{4}).-(?(1)\1|\d{1})
2015년 4월 23일 목요일
[리뷰][서적] C 프로그래밍의 이해 - C언어 입문서로 손색없는 서적
C 프로그래밍의 이해 - 스티브 오울린 지음, 최성원 옮김/한빛미디어 |
C언어를 공부하고 싶어서 처음에는 'new C언어 입문(중급편) - 링크' 을 읽어보았다.
경력 5년이 넘는 Java 개발자로 일하다보니 Java와의 다른점에 대해서 이해하기 쉽고 깊은 내용까지 다루고 있어서 좋았지만 코딩한줄 없이 이론으로만 보다보니 금방 잊어버리게 되고 간단한 거라도 코딩을 하기 힘들었다.
뭔가 예제를 따라하면서 코딩을 하며 공부 할 수 있는 서적이 없을까 하다가 'C 프로그래밍의 이해' 라는 책을 중고로 구입하게 되었다.
그리 얇지도 않은 책이지만 예제,질문 문제가 그리 많지는 않아서 문제에 대해서 깊이있게 생각하고 천천히 진도를 나가도 책한권을 다 보는데 오래 걸리지 않아서 그리 지치지 않는다.
하지만 C를 이용해 생각하는 프로그램을 원활히 만들기 위해서는 스스로에게 질문을 내어 좀더 연습을 하던가 다른 책을 보면서 문제를 조금 풀어보는게 도움이 되지 않을까 싶다.
PS.
프로그램을 하나도 모르는 완전초보에게는 조금 어렵지 않을까 싶다.
차라리 다른 쉬운 국내 도서를 권장한다.
장점
1. [예제] 를 따라하면서 내가 코딩한 결과를 보고 조금씩 수정해 가며 더 자세히 알 수 있었다.
2. [질문] 에 대해서 정답을 보기전에 먼저 생각하고 코드를 디버깅 해가면서 좀더 자세히 이해할 수 있었다.
단점
1. 챕터의 마지막에 [연습] 문제가 있는데 문제의 난이도가 상당히 높으며 정답이 따로 적혀있는건 아니었다.
2. 실질적으로 코딩을 많이 하면서 실력을 쌓고 싶다면 열혈강의같은 국내 도서를 구입하는게 좀더 좋지 않을까 싶다.
이 책을 다 본 후 new C언어 입문[중급편] - 링크 서적을 보면 크게 도움이 될 듯 하다.
2015년 4월 22일 수요일
[리뷰][서적] Pro HTML5 Programming - 단시간에 HTML5 에 대한 이해를 할 수 있는 추천 서적
개정판
프로 HTML5 프로그래밍 - 피터 러버스 지음/미래웹기술연구소 |
구판
프로 HTML5 프로그래밍 - 피터 러버스 외 지음, 이준.정승희 옮김/위키북스 |
내가 읽은건 구판이긴 한데.. 책의 내용이 꽤 맘에 든다.
개정판과 구판의 페이지가 100페이지 정도 차이가 나던데..
개정판도 다시 읽어보고 싶긴 하다.
내가 맘에 들었던 부분은 하이브리드 앱을 제작하려는데 있어서 기술검토를 해야 하는데 몇개는 간단한 예제로 봐서 대충은 알고 있었지만
웹소켓(WebSocket), 웹워커(Web Worker) 의 경우에는 예제도 본적이 없을뿐더러..
웹워커 같은건 개념 자체를 모르는 상태였다.
예제 소스가 있기는 하지만 간단한 소스여서 기술에 대해서만 알려 한다면 구지 PC로 예제를 돌려볼 필요가 없어서 출퇴근 시간에 읽기에 좋다.
1. postMessage : 다른 도메인(crossdomain)에 대해 메시지 정송
2. XMLHttpRequest2 : 다른 도메인(crossdomain)에 대해 Ajax
3. 웹소켓 : 브라우저에서 소켓 사용
4. 웹워커 : 브라우저의 반응속도가 끊기지 않게 백그라운드에서 로직 처리
5. 오프라인 웹 애플리케이션
위의 기술들에 대해서 대충 어떤 개념이고 어떤때 사용을 해야 하는 것에 대해서 알게 되었다.
그리고 종종 틀린것도 있던거 같지만 지원하는 브라우저의 종류를 알려주어 해당 기술을 사용 시 브라우저가 특정버전 이하를 지원해야 한다면 사용할 수 없는 것 까지 기술되어 있었다.
장점
1. HTML5 의 개념에 대한 빠른 이해를 할 수 있다.
2. 예제가 간단해서 출퇴근 시간에 읽을 정도의 내용이다.
단점
1. 예제가 간단해서 실제 구현시에는 레퍼런스를 보거나 다른 예제를 참조해야 할 것 같다.
2. CSS3 또한 HTML5 의 개념으로 분류가 되는걸로 알고있는데 CSS3에 대한 설명은 거의 없다. CSS를 통한 애니메이션이나 WebGL관련 기술은 다른 서적을 참고해야 할 듯 하다.
라벨:
도서
,
서적
,
웹소켓
,
웹워커
,
추천
,
크로스도메인
,
ajax
,
crossdomain
,
html5
,
postMessage
,
web worker
,
websocket
,
XMLHttpRequest2
2015년 4월 15일 수요일
[Youtube] Player API 를 활용한 유튜브 동영상 컨트롤
다음이나 네이버와 같이 자체적으로 스트리밍 서비스를 하지 않고서는 대부분의 회사는 Google 에서 운영하는 Youtube 에 동영상을 많이 올려서 홍보를 많이 한다.
최근 Youtube 의 동영상을 제어해야 할 일이 있어서 알아본 것을 정리하려 한다.
Youtube 동영상을 컨트롤 할 수 있는 Player API 는 다음과 같다.
1. Android API
2. IFrame API
5. iOS API
1004lucifer
===========================
2019.06.16 내용 업데이트
- IFrame, Javascript API가 통합되었다. 이제는 Javascript API를 사용하더라도 자동으로 IFrame 이 생성이 된다.
- 아래의 데모(jsfiddle)의 Result를 클릭 시 작동 모습을 볼 수 있다.
(현재 크롬에서 Result에서 기능이 작동 안하는 경우가 종종 있는데.. 다른 탭으로 갔다가 다시 Result로 돌아오면 정상적으로 작동된다.
Youtube 공식 샘플사이트(링크)도 문제가 있는걸로 봐서는 현재 버전의 크롬에 문제가 있는게 아닐까 생각이 들며, 추후 개선되지 않을까 싶다.
음소거(mute) 설정을 하게되면 문제가 없는데 아래의 크롬 autoplay 기능이 추가되면서 버그가 발생한게 아닐까 싶다.)
- 크롬 autoplay 정책이 변경되었다. (2018년 04월)
https://developers.google.com/web/updates/2017/09/autoplay-policy-changes#iframe
1) 음소거된 자동재생은 무조건 허용됨.
1) iframe 사용시 allow="autoplay; fullscreen" 속성을 추가하면 소리있는 자동재생 및 풀스크린 허용됨.
============================
IFrame API 사용방법
참조: https://developers.google.com/youtube/iframe_api_reference
- https://developers.google.com/youtube/player_parameters?playerVersion=HTML5&hl=ko
1. <iframe> 태그에 이벤트 연동
- iframe 태그의 src에 enablejsapi 값을 꼭 넣어주어야 한다. (한참 삽질을..;;)
<!-- User: 1004lucifer Date: 2015. 4. 14. --> <!DOCTYPE html> <html> <head lang="en"> <meta charset="UTF-8"> <title></title> </head> <body> <!-- 주의: src링크에 enablejsapi=1 을 꼭 넣어주어야 한다. --> <iframe id="gangnamStyleIframe" width="560" height="315" src="https://www.youtube.com/embed/9bZkp7q19f0?rel=0&enablejsapi=1" frameborder="0" allow="autoplay; fullscreen" allowfullscreen></iframe> <script type="text/javascript"> /** * Youtube API 로드 */ var tag = document.createElement('script'); tag.src = "https://www.youtube.com/iframe_api"; var firstScriptTag = document.getElementsByTagName('script')[0]; firstScriptTag.parentNode.insertBefore(tag, firstScriptTag); /** * onYouTubeIframeAPIReady 함수는 필수로 구현해야 한다. * 플레이어 API에 대한 JavaScript 다운로드 완료 시 API가 이 함수 호출한다. * 페이지 로드 시 표시할 플레이어 개체를 만들어야 한다. */ var player; function onYouTubeIframeAPIReady() { player = new YT.Player('gangnamStyleIframe', { // height: '315', // <iframe> 태그 지정시 필요없음 // width: '560', // <iframe> 태그 지정시 필요없음 // videoId: '9bZkp7q19f0', // <iframe> 태그 지정시 필요없음 // playerVars: { // <iframe> 태그 지정시 필요없음 // controls: '2' // }, events: { 'onReady': onPlayerReady, // 플레이어 로드가 완료되고 API 호출을 받을 준비가 될 때마다 실행 'onStateChange': onPlayerStateChange // 플레이어의 상태가 변경될 때마다 실행 } }); } function onPlayerReady(event) { console.log('onPlayerReady 실행'); // 플레이어 자동실행 (주의: 모바일에서는 자동실행되지 않음) event.target.playVideo(); } var playerState; function onPlayerStateChange(event) { playerState = event.data == YT.PlayerState.ENDED ? '종료됨' : event.data == YT.PlayerState.PLAYING ? '재생 중' : event.data == YT.PlayerState.PAUSED ? '일시중지 됨' : event.data == YT.PlayerState.BUFFERING ? '버퍼링 중' : event.data == YT.PlayerState.CUED ? '재생준비 완료됨' : event.data == -1 ? '시작되지 않음' : '예외'; console.log('onPlayerStateChange 실행: ' + playerState); } </script> </body> </html>
1004lucifer
2. <iframe> 태그를 <div> 태그로 치환하는 방법
<!-- User: 1004lucifer Date: 2015. 4. 14. --> <!DOCTYPE html> <html> <head lang="en"> <meta charset="UTF-8"> <title></title> </head> <body> <div id="gangnamStyleIframe"></div> <script type="text/javascript"> /** * Youtube API 로드 */ var tag = document.createElement('script'); tag.src = "https://www.youtube.com/iframe_api"; var firstScriptTag = document.getElementsByTagName('script')[0]; firstScriptTag.parentNode.insertBefore(tag, firstScriptTag); /** * onYouTubeIframeAPIReady 함수는 필수로 구현해야 한다. * 플레이어 API에 대한 JavaScript 다운로드 완료 시 API가 이 함수 호출한다. * 페이지 로드 시 표시할 플레이어 개체를 만들어야 한다. */ var player; function onYouTubeIframeAPIReady() { player = new YT.Player('gangnamStyleIframe', { height: '315', // <iframe> 태그 지정시 필요없음 width: '560', // <iframe> 태그 지정시 필요없음 videoId: '9bZkp7q19f0', // <iframe> 태그 지정시 필요없음 playerVars: { // <iframe> 태그 지정시 필요없음 controls: '2' }, events: { 'onReady': onPlayerReady, // 플레이어 로드가 완료되고 API 호출을 받을 준비가 될 때마다 실행 'onStateChange': onPlayerStateChange // 플레이어의 상태가 변경될 때마다 실행 } }); } function onPlayerReady(event) { console.log('onPlayerReady 실행'); // 플레이어 자동실행 (주의: 모바일에서는 자동실행되지 않음) event.target.playVideo(); } var playerState; function onPlayerStateChange(event) { playerState = event.data == YT.PlayerState.ENDED ? '종료됨' : event.data == YT.PlayerState.PLAYING ? '재생 중' : event.data == YT.PlayerState.PAUSED ? '일시중지 됨' : event.data == YT.PlayerState.BUFFERING ? '버퍼링 중' : event.data == YT.PlayerState.CUED ? '재생준비 완료됨' : event.data == -1 ? '시작되지 않음' : '예외'; console.log('onPlayerStateChange 실행: ' + playerState); } </script> </body> </html>
위의 기능을 기본으로 응용을 하면 아래와 같이 제작이 가능하다.
<!-- User: 1004lucifer Date: 2015. 4. 14. --> <!DOCTYPE html> <html> <head lang="en"> <meta charset="UTF-8"> <title></title> </head> <body> <div id="gangnamStyleIframe"></div> <br/><br/> <button type="button" onclick="playYoutube();">Play</button> <button type="button" onclick="pauseYoutube();">Pause</button> <button type="button" onclick="stopYoutube();">Stop</button> <script type="text/javascript"> /** * Youtube API 로드 */ var tag = document.createElement('script'); tag.src = "https://www.youtube.com/iframe_api"; var firstScriptTag = document.getElementsByTagName('script')[0]; firstScriptTag.parentNode.insertBefore(tag, firstScriptTag); /** * onYouTubeIframeAPIReady 함수는 필수로 구현해야 한다. * 플레이어 API에 대한 JavaScript 다운로드 완료 시 API가 이 함수 호출한다. * 페이지 로드 시 표시할 플레이어 개체를 만들어야 한다. */ var player; function onYouTubeIframeAPIReady() { player = new YT.Player('gangnamStyleIframe', { height: '315', // <iframe> 태그 지정시 필요없음 width: '560', // <iframe> 태그 지정시 필요없음 videoId: '9bZkp7q19f0', // <iframe> 태그 지정시 필요없음 playerVars: { // <iframe> 태그 지정시 필요없음 controls: '2' }, events: { 'onReady': onPlayerReady, // 플레이어 로드가 완료되고 API 호출을 받을 준비가 될 때마다 실행 'onStateChange': onPlayerStateChange // 플레이어의 상태가 변경될 때마다 실행 } }); } function onPlayerReady(event) { console.log('onPlayerReady 실행'); // 플레이어 자동실행 (주의: 모바일에서는 자동실행되지 않음) // event.target.playVideo(); } var playerState; function onPlayerStateChange(event) { playerState = event.data == YT.PlayerState.ENDED ? '종료됨' : event.data == YT.PlayerState.PLAYING ? '재생 중' : event.data == YT.PlayerState.PAUSED ? '일시중지 됨' : event.data == YT.PlayerState.BUFFERING ? '버퍼링 중' : event.data == YT.PlayerState.CUED ? '재생준비 완료됨' : event.data == -1 ? '시작되지 않음' : '예외'; console.log('onPlayerStateChange 실행: ' + playerState); // 재생여부를 통계로 쌓는다. collectPlayCount(event.data); } function playYoutube() { // 플레이어 자동실행 (주의: 모바일에서는 자동실행되지 않음) player.playVideo(); } function pauseYoutube() { player.pauseVideo(); } function stopYoutube() { player.seekTo(0, true); // 영상의 시간을 0초로 이동시킨다. player.stopVideo(); } var played = false; function collectPlayCount(data) { if (data == YT.PlayerState.PLAYING && played == false) { // todo statistics played = true; console.log('statistics'); } } </script> </body> </html>
1004lucifer
고급기능
- 플레이할 영상을 대기열에 넣어서 차례로 플레이가 가능하다.
<!-- User: 1004lucifer Date: 2015. 4. 14. --> <!DOCTYPE html> <html> <head lang="en"> <meta charset="UTF-8"> <title></title> </head> <body> <div id="gangnamStyleIframe"></div> <br/><br/> <button type="button" onclick="playYoutube();">Play</button> <button type="button" onclick="pauseYoutube();">Pause</button> <button type="button" onclick="stopYoutube();">Stop</button> <button type="button" onclick="changeVideoAndStart();">ChangeVideo And Start</button> <button type="button" onclick="changeVideoObjectAndStart();">ChangeVideoObject And Start</button> <button type="button" onclick="changeVideoListAndStart();">ChangeVideoList And Start</button> <button type="button" onclick="changeVideoListObjectAndStart();">ChangeVideoListObject(Video IDs) And Start</button> <button type="button" onclick="changeVideoListObjectAndStart2();">ChangeVideoListObject(playlist ID) And Start</button> <script type="text/javascript"> /** * Youtube API 로드 */ var tag = document.createElement('script'); tag.src = "http://www.youtube.com/iframe_api"; var firstScriptTag = document.getElementsByTagName('script')[0]; firstScriptTag.parentNode.insertBefore(tag, firstScriptTag); /** * onYouTubeIframeAPIReady 함수는 필수로 구현해야 한다. * 플레이어 API에 대한 JavaScript 다운로드 완료 시 API가 이 함수 호출한다. * 페이지 로드 시 표시할 플레이어 개체를 만들어야 한다. */ var player; function onYouTubeIframeAPIReady() { player = new YT.Player('gangnamStyleIframe', { height: '315', // <iframe> 태그 지정시 필요없음 width: '560', // <iframe> 태그 지정시 필요없음 videoId: '9bZkp7q19f0', // <iframe> 태그 지정시 필요없음 playerVars: { // <iframe> 태그 지정시 필요없음 controls: '2' }, events: { 'onReady': onPlayerReady, // 플레이어 로드가 완료되고 API 호출을 받을 준비가 될 때마다 실행 'onStateChange': onPlayerStateChange // 플레이어의 상태가 변경될 때마다 실행 } }); } function onPlayerReady(event) { console.log('onPlayerReady 실행'); } var playerState; function onPlayerStateChange(event) { playerState = event.data == YT.PlayerState.ENDED ? '종료됨' : event.data == YT.PlayerState.PLAYING ? '재생 중' : event.data == YT.PlayerState.PAUSED ? '일시중지 됨' : event.data == YT.PlayerState.BUFFERING ? '버퍼링 중' : event.data == YT.PlayerState.CUED ? '재생준비 완료됨' : event.data == -1 ? '시작되지 않음' : '예외'; console.log('onPlayerStateChange 실행: ' + playerState); // 재생여부를 통계로 쌓는다. collectPlayCount(event.data); } function playYoutube() { // 플레이어 자동실행 (주의: 모바일에서는 자동실행되지 않음) player.playVideo(); } function pauseYoutube() { player.pauseVideo(); } function stopYoutube() { player.seekTo(0, true); // 영상의 시간을 0초로 이동시킨다. player.stopVideo(); } var played = false; function collectPlayCount(data) { if (data == YT.PlayerState.PLAYING && played == false) { // todo statistics played = true; console.log('statistics'); } } /** * loadVideoById 함수는 지정한 동영상을 로드하고 재생한다. * 인수구문: loadVideoByUrl(mediaContentUrl:String, startSeconds:Number, suggestedQuality:String):Void * 개체구문: loadVideoByUrl({mediaContentUrl:String, startSeconds:Number, endSeconds:Number, suggestedQuality:String}):Void * loadVideoById 함수 뿐만 아니라 다른 대체적인 함수들도 개체구문이 기능이 더 많다. */ function changeVideoAndStart() { player.loadVideoById("iCkYw3cRwLo", 0, "large"); } function changeVideoObjectAndStart() { // 0초부터 10초까지 재생을 시킨다. player.loadVideoById({ 'videoId': 'bHQqvYy5KYo', 'startSeconds': 0, 'endSeconds': 10 }); } /** * loadPlaylist 함수는 지정한 재생목록을 로드하고 재생한다. * 인수구문: loadPlaylist(playlist:String|Array, index:Number, startSeconds:Number, suggestedQuality:String):Void * 개체구문: loadPlaylist({list:String, listType:String, index:Number, startSeconds:Number, suggestedQuality:String}):Void * [주의: 개체구문의 loadPlaylist 함수에서의 재생목록ID 와 동영상ID 의 사용방법이 다르다.] */ function changeVideoListAndStart() { player.loadPlaylist(['wcLNteez3c4', 'LOsNP2D2kSA', 'rX372ZwXOEM'], 0, 0, 'large'); } function changeVideoListObjectAndStart() { player.loadPlaylist({ 'playlist': ['9HPiBJBCOq8', 'Mp4D0oHEnjc', '8y1D8KGtHfQ', 'jEEF_50sBrI'], 'listType': 'playlist', 'index': 0, 'startSeconds': 0, 'suggestedQuality': 'small' }); } function changeVideoListObjectAndStart2() { player.loadPlaylist({ 'list': 'UUPW9TMt0le6orPKdDwLR93w', 'listType': 'playlist', 'index': 0, 'startSeconds': 0, 'suggestedQuality': 'small' }); } </script> </body> </html>
PS.
API 문서에 loadPlaylist 에 대한 설명 올바르지 않다.
링크: LoadPlaylist function issues of the IFrame API (IFrame API 의 loadPlaylist 함수 이슈)
1004lucifer
자동재생 및 스크립트 재생
- 모바일에서는 HTML5 사용자 상호작용(터치)이 없으면 자동재생을 지원하지 않는다고 한다. (링크)
========================
2019.06.16 추가 (Android 에서 테스트)
- 현재 크롬에서는 음소거(mute) 설정 시 모바일에서도 자동재생이 가능하다.
1) 일단 재생을 시키고 스크립트로 player.unMute() 로 음소거를 해제시키니 사용자의 상호작용이 없는 상태에서는 영상이 중지되었다.
2) touchstart/touchend 이벤트가 발생 시 음소거 해제를 시키도록 테스트해봤는데, touchmove 이벤트가 발생하면 역시 영상이 중지되었다.
결국엔 화면을 터치만하면 소리가 나며 정상적으로 재생되지만 스크롤을 하게되면 영상이 멈추게되어 아직까지 우회할 수 있는 방법은 찾지 못했다.
========================
[Youtube] LoadPlaylist function issues of the IFrame API (IFrame API 의 loadPlaylist 함수 이슈)
문제
Youtube API (https://developers.google.com/youtube/iframe_api_reference)
를 보던중 문서에 표기되어 있는대로 프로그램을 작성했는데 아래의 함수가 작동이 되지 않는 문제가 있었다.
[정확히는 재생목록ID(playlist ID) 를 사용하면 잘 되지만 동영상ID(video IDs)를 사용하면 작동하지 않았다.]
player.cuePlaylist({listType:String,
list:String,
index:Number,
startSeconds:Number,
suggestedQuality:String}):Void
player.loadPlaylist({list:String,
listType:String,
index:Number,
startSeconds:Number,
suggestedQuality:String}):Void
인터넷을 찾아보니 다른사람들도 나와 비슷한 문제가 있는 듯 싶다.
1. http://stackoverflow.com/questions/9148227/youtube-player-api-list-with-videos
2. https://groups.google.com/forum/#!topic/youtube-api-gdata/8d0XsJwQ9dw
3. https://code.google.com/p/gdata-issues/issues/detail?id=6303
해결방법
아래와 같이 작성 시 정상적으로 동작한다.
(SyntaxHighlighter 문제로 <br/> 태그가 보이지 않는다.)
<!-- User: 1004lucifer Date: 2015. 4. 15. --> <!DOCTYPE html> <html> <head lang="en"> <meta charset="UTF-8"> <title></title> </head> <body> <div id="gangnamStyleIframe"></div> <br/><br/> <button type="button" onclick="playYoutube();">Play</button> <br/> <button type="button" onclick="pauseYoutube();">Pause</button> <br/> <button type="button" onclick="stopYoutube();">Stop</button> <br/> <button type="button" onclick="loadPlaylist();">player.loadPlaylist</button> <br/> <button type="button" onclick="loadPlaylist_playlist_id();">player.loadPlaylist - playlist ID</button> <br/> <button type="button" onclick="loadPlaylist_video_ids();">player.loadPlaylist - video IDs</button> <script type="text/javascript"> var tag = document.createElement('script'); tag.src = "http://www.youtube.com/iframe_api"; var firstScriptTag = document.getElementsByTagName('script')[0]; firstScriptTag.parentNode.insertBefore(tag, firstScriptTag); var player; function onYouTubeIframeAPIReady() { player = new YT.Player('gangnamStyleIframe', { height: '315', width: '560', videoId: '9bZkp7q19f0', playerVars: { controls: '2' }, events: { 'onReady': onPlayerReady, 'onStateChange': onPlayerStateChange } }); } function onPlayerReady(event) { console.log('onPlayerReady 실행'); } var playerState; function onPlayerStateChange(event) { playerState = event.data == YT.PlayerState.ENDED ? '종료됨' : event.data == YT.PlayerState.PLAYING ? '재생 중' : event.data == YT.PlayerState.PAUSED ? '일시중지 됨' : event.data == YT.PlayerState.BUFFERING ? '버퍼링 중' : event.data == YT.PlayerState.CUED ? '재생준비 완료됨' : event.data == -1 ? '시작되지 않음' : '예외'; console.log('onPlayerStateChange 실행: ' + playerState); } function playYoutube() { player.playVideo(); } function pauseYoutube() { player.pauseVideo(); } function stopYoutube() { player.seekTo(0, true); player.stopVideo(); } function loadPlaylist() { player.loadPlaylist(['wcLNteez3c4', 'LOsNP2D2kSA', 'rX372ZwXOEM'], 0, 0, 'large'); } function loadPlaylist_playlist_id() { player.loadPlaylist({ 'list': 'UUPW9TMt0le6orPKdDwLR93w', 'listType': 'playlist', 'index': 0, 'startSeconds': 0, 'suggestedQuality': 'small' }); } function loadPlaylist_video_ids() { player.loadPlaylist({ 'playlist': ['9HPiBJBCOq8', 'Mp4D0oHEnjc', '8y1D8KGtHfQ', 'jEEF_50sBrI'], 'listType': 'playlist', 'index': 0, 'startSeconds': 0, 'suggestedQuality': 'small' }); } </script> </body> </html>
PS.
현재상황에서의 API문서(링크) 대로 작성 시 동영상ID(video IDs) 를 실행할 수 없다.
[API문서가 잘못된건지.. API가 잘못된건지 잘 모르겠다.]
참고: http://www.cgis.biz/others/youtube/player8/
관련링크: Player API 를 활용한 유튜브 동영상 컨트롤
라벨:
에러
,
오류
,
유튜브
,
API
,
Error
,
IFrame API
,
issue
,
loadPlaylist
,
not working
,
player.loadPlaylist
,
playlist ID
,
video IDs
,
Youtube
[C] 세그멘테이션 오류 (core dumped) 발생 시 coredump 파일이 생성되지 않는 경우
우분투(Ubuntu) 로 C언어 공부 시
세그멘테이션오류 (core dumped) [Segmentation fault]
라는 메시지와 함께 프로그램이 종료 시 gdb 를 통해서 디버깅을 하라고 하는데 위와 같이 오류가 발생했음에도 core 파일이 생성되지 않아서 좀 찾아봤다.
해결방법
core file size (blocks, -c) 0
data seg size (kbytes, -d) unlimited
scheduling priority (-e) 0
file size (blocks, -f) unlimited
pending signals (-i) 30938
max locked memory (kbytes, -l) 64
max memory size (kbytes, -m) unlimited
open files (-n) 1024
pipe size (512 bytes, -p) 8
POSIX message queues (bytes, -q) 819200
real-time priority (-r) 0
stack size (kbytes, -s) 8192
cpu time (seconds, -t) unlimited
max user processes (-u) 30938
virtual memory (kbytes, -v) unlimited
file locks (-x) unlimited
lucifer@lucifer-Vostro-V13:~/test/C$
lucifer@lucifer-Vostro-V13:~/test/C$ ulimit -c unlimited
lucifer@lucifer-Vostro-V13:~/test/C$
lucifer@lucifer-Vostro-V13:~/test/C$ ulimit -a
core file size (blocks, -c) unlimited
data seg size (kbytes, -d) unlimited
scheduling priority (-e) 0
file size (blocks, -f) unlimited
pending signals (-i) 30938
max locked memory (kbytes, -l) 64
max memory size (kbytes, -m) unlimited
open files (-n) 1024
pipe size (512 bytes, -p) 8
POSIX message queues (bytes, -q) 819200
real-time priority (-r) 0
stack size (kbytes, -s) 8192
cpu time (seconds, -t) unlimited
max user processes (-u) 30938
virtual memory (kbytes, -v) unlimited
file locks (-x) unlimited
lucifer@lucifer-Vostro-V13:~/test/C$
위와같이 작업 후 프로그램 실행 후
세그멘테이션오류 (core dumped) [Segmentation fault]
메시지가 발생한다면 기본적으로 프로그램이 있는 디렉토리에 core 파일이 생성된 것을 확인할 수 있다.
위의 설정만으로는 다른 쉘에서는 적용되지 않는다.
컴퓨터 기동 시 모든 쉘에서 위의 설정이 적용되도록 하려면 다음과 같이 작업하면 된다.
$ vi ~/.bashrc
=================================
# 파일내용의 제일 처음이나 제일 마지막에 아래의 문자열을 입력한다.
ulimit -c unlimited정상적으로 적용이 된다면 다음과 같이 나오게 된다.
lucifer@lucifer-Vostro-V13:~/test/C/testCore$ ls -l
합계 16
-rw-r--r-- 1 lucifer lucifer 13 4월 15 01:33 numbers.dat
-rwxr-xr-x 1 lucifer lucifer 11793 4월 15 01:32 search1
lucifer@lucifer-Vostro-V13:~/test/C/testCore$
lucifer@lucifer-Vostro-V13:~/test/C/testCore$ ./search1
세그멘테이션 오류 (core dumped)
lucifer@lucifer-Vostro-V13:~/test/C/testCore$
lucifer@lucifer-Vostro-V13:~/test/C/testCore$ ls -l
합계 404
-rw------- 1 lucifer lucifer 397312 4월 15 01:33 core
-rw-r--r-- 1 lucifer lucifer 13 4월 15 01:33 numbers.dat
-rwxr-xr-x 1 lucifer lucifer 11793 4월 15 01:32 search1
lucifer@lucifer-Vostro-V13:~/test/C/testCore$
2015년 4월 14일 화요일
[Android][CSS] Android 4.0.x 이하에서 CSS3 (transition, transform) 속성이 적용되지 않는경우
증상
Android 4.4.x
- OEM(기본) Browser, Chrome 모두 transition 속성이 정상적으로 적용됨
Android 4.0.x, 4.1.x
- transition 속성이 Chrome 에서는 적용되나 OEM Browser 에서만 적용되지 않음.
수정방법
Android 4.1.x 및 4.0.x 이하의버전에서는 OEM Browser에서 transition 속성을 사용시 webkit prefix 를 붙인 송성을 추가해야 정상적으로 작동을 한다.
(4.2.x 및 4.3.x 에서는 테스트를 해보지 못했다.)
원래 속성
transition-duration: 1s;
수정 속성
transition-duration: 1s;
-webkit-transition-duration: 1s;
PS.
Webkit 에서는 -webkit-
Firefox 에서는 -moz-
Opera 에서는 -o-
를 붙여줘야 하는걸 알고있긴 했지만 프로토타입을 만드는 거라 대충만들었더니 생각도 못하고 삽질을 했네;;
라벨:
-webkit-transition
,
4.0
,
4.1
,
4.2
,
4.4
,
Android
,
CSS
,
transition
,
transition-duration
2015년 4월 13일 월요일
[Android Studio] Module Import 할 시 "Specify location of the Gradle or Android Eclipse project" 메시지가 나오는 경우
환경: Android Studio 1.1.0
증상
기존의 Module(또는 Eclipse의 Project) 를 Import 하려 할 시 아래와 같이
Specify location of the Gradle or Android Eclipse project
라는 에러가 발생하며 Import 를 할 수 없다.
원인
Android Studio 버전
0.X 에서는 셋팅방법이 IntelliJ Ultimate 와 비슷했지만
정식으로 나온 1.X 버전에서는 Gradle 이 default 로 사용이 된다.
Gradle 을 사용하지 않는 프로젝트나 Android Eclipse Project 의 구조와 다르면
Specify location of the Gradle or Android Eclipse project
라는 에러메시지를 보여준다.
PS.
https://github.com/1004lucifer/Android_HelloWorld
위의 소스를 다운받아서 아무런 수정도 하지 않고 Import 하면 정상적으로 Import 할 수 있지만 src 디렉토리 이름을 java 로 변경을 하게되면
Specify location of the Gradle or Android Eclipse project
에러메시지를 보여준다.
해결방법
1. Gradle 사용을 할 수 있도록 build.gradle 파일을 작성해서 프로젝트에 넣어준다.
2. Android Eclipse project 에 맞게 형식을 변경해 준다.
3. IntelliJ(Ultimate or Community) 의 셋팅방법으로 변경시켜준다.
- 링크: [Android Studio] Module Import 시 Gradle 없이 설정하는 방법
라벨:
모듈
,
안드로이드
,
Android
,
Android Studio
,
eclipse
,
Gradle
,
Import
,
IntelliJ
,
Module
,
Specify location of the Gradle or Android Eclipse project
[Android Studio] Module Import 시 Gradle 없이 설정하는 방법
환경: Android Studio 1.1.0
Android Studio 가 정식버전으로 업데이트 되면서 Gradle 이 default 사용으로 변경 되었다.
나의 경우에는 Gradle 을 사용해 본적이 없기도 하고..
IntelliJ Ultimate 버전에서 셋팅했던 방법으로 하고 싶어서 다음과 같이 작업을 했다.
방법
1. Project Structure 에서 다음과 같이 프로젝트 생성시 만들어진 Module 을 삭제한다.
2. Module 을 삭제해도 해당 모듈을 디스크에 남아있다는 경고창 (YES 선택)
3. Module 이 삭제된 것을 확인 후 OK 버튼 클릭
4. 다시 Project Structure 를 열면 화면이 바뀌어 있음을 볼 수 있다.
5. Module 탭 선택 => 플러스(+)버튼 선택 => 'Import Module' 선택 => 소스 path 선택
6. 다음과 같이 작업해 준다.
7. Module 이 정상적으로 Import 되었다.
(스크린샷의 Module 은 간단한 소스(링크에서 src 디렉토리 이름을 java로 변경) 라서 자동으로 셋팅이 완료되었다. 복잡한 구조의 소스의 경우 추가로 수동설정이 필요할 수 있다.)
위와같은 설정으로 Gradle 설정이나 Android Eclipse project 형식에 맞지 않는다는 에러메시지를 볼 필요 없이 Module Import 가 가능해졌다.
연관포스트
1. [Android Studio] Module Import 할 시 "Specify location of the Gradle or Android Eclipse project" 메시지가 나오는 경우
[Android Studio] Project 에서 assets 및 res 디렉토리가 보이지 않는경우
환경 : Anroid Studio version: 1.1.0
문제
프로젝트를 생성 후 아래와 같이
assets 및 res 디렉토리 라던지 AndroidManifest.xml 파일이 보이지 않아서 왜그런지 몰라서 엉뚱한 곳을 한참 삽질했던 것 같다.
해결방법
너무나도 허탈했다. 다음과 같이 보여질 유형을 변경하면 되는 것이었다.
PS.
위의 Project Directory 가 저렇게 나오는 이유는 Android Studio 가 정식버전이 되면서 부터 Project 를 새로 생성 시 default 로 Gradle 을 사용하게 되면서 구조가 저렇게 바뀌어 버렸다.
라벨:
디렉토리
,
Android
,
Android Studio
,
AndroidManifest.xml
,
assets
,
directory
,
Gradle
,
hidden
,
IntelliJ
,
res
2015년 4월 8일 수요일
[Java][Gson] Gson 사용방법(예제) 정리 - Java 에서의 JSON 추천 라이브러리
Google Library 중에 하나인 Gson
Java <=> JSON 으로 사용되는 라이브러리이며 많이들 쓰이는듯 하다.
사이트: https://code.google.com/p/google-gson/
API: http://google-gson.googlecode.com/svn/trunk/gson/docs/javadocs/index.html
User Guide: https://sites.google.com/site/gson/gson-user-guide/
이것저것 알아봤는데 아까울정도로 열심히 알아봐서 이렇게 정리를..;;
Company.java
import java.util.ArrayList; import java.util.List; /** * Created by 1004lucifer on 2015-04-08. */ public class Company { private String name; private List<Person> employees; public String getName() { return name; } public void setName(String name) { this.name = name; } public List<Person> getEmployees() { return employees; } public void setEmployees(List<Person> employees) { this.employees = employees; } public static class Person { private String name; private String age; private String sex; public String getName() { return name; } public void setName(String name) { this.name = name; } public String getAge() { return age; } public void setAge(String age) { this.age = age; } public String getSex() { return sex; } public void setSex(String sex) { this.sex = sex; } public String toString() { return "name: " + name + "\tage: " + age + "\tsex: " + sex; } } public static Company getCompanyDummy() { Company company = new Company(); company.setName("1004lucifer's Company"); List<Company.Person> personList = new ArrayList<Person>(); Company.Person person = new Person(); person.setName("1004lucifer"); person.setAge("30"); person.setSex("M"); personList.add(person); person = new Person(); person.setName("vvoei"); person.setAge("29"); person.setSex("M"); personList.add(person); person = new Person(); person.setName("John"); person.setSex("M"); personList.add(person); person = new Person(); person.setName("Jane"); person.setAge("20"); personList.add(person); person = new Person(); personList.add(person); company.setEmployees(personList); return company; } }
단순 JavaObject(Class) <=> JSON 변환 방법
1004lucifer
TestGsonUser.java
import com.google.gson.Gson; import com.google.gson.GsonBuilder; import java.util.List; /** * Created by 1004lucifer on 2015-04-08. */ public class TestGsonUser { public static void main(String[] argv) { Company company = Company.getCompanyDummy(); System.out.println("========= Object => Json =========="); String company2Json = new Gson().toJson(company); System.out.println(company2Json); System.out.println("========= Json => Object ========="); Company json2Company = new Gson().fromJson(company2Json, Company.class); printCompanyObject(json2Company); System.out.println("========= Object => Json ========="); String company2JsonIsNull = new GsonBuilder().serializeNulls().create().toJson(company); System.out.println(company2JsonIsNull); System.out.println("========= Json => Object ========="); Company json2CompanyIsNull = new Gson().fromJson(company2Json, Company.class); printCompanyObject(json2CompanyIsNull); } private static void printCompanyObject(Company company) { List<Company.Person> personList = company.getEmployees(); System.out.println("userName: " + company.getName()); for (Company.Person person : personList) { System.out.println(person); } } }
결과
{"name":"1004lucifer\u0027s Company","employees":[{"name":"1004lucifer","age":"30","sex":"M"},{"name":"vvoei","age":"29","sex":"M"},{"name":"John","sex":"M"},{"name":"Jane","age":"20"},{}]}
========= Json => Object =========
userName: 1004lucifer's Company
name: 1004lucifer age: 30 sex: M
name: vvoei age: 29 sex: M
name: John age: null sex: M
name: Jane age: 20 sex: null
name: null age: null sex: null
========= Object => Json =========
{"name":"1004lucifer\u0027s Company","employees":[{"name":"1004lucifer","age":"30","sex":"M"},{"name":"vvoei","age":"29","sex":"M"},{"name":"John","age":null,"sex":"M"},{"name":"Jane","age":"20","sex":null},{"name":null,"age":null,"sex":null}]}
========= Json => Object =========
userName: 1004lucifer's Company
name: 1004lucifer age: 30 sex: M
name: vvoei age: 29 sex: M
name: John age: null sex: M
name: Jane age: 20 sex: null
name: null age: null sex: null
String JSON 편집 방법
1004lucifer
Java Class 가 없는경우 JSON 값은 다음과 같이 작업을 했다.
TestGsonSetJsonValue.java
import com.google.gson.*; import com.skt.tservice.network.module.EncryptSDK; import java.util.Iterator; import java.util.Map; /** * Created by 1004lucifer on 2015-04-08. */ public class TestGsonSetJsonValue { public static void main(String[] argv) { String companyJson= "{\"name\":\"1004lucifer\\u0027s Company\",\"employees\":[{\"name\":\"1004lucifer\",\"age\":\"30\",\"sex\":\"M\"},{\"name\":\"vvoei\",\"age\":\"29\",\"sex\":\"M\"},{\"name\":\"John\",\"sex\":\"M\"},{\"name\":\"Jane\",\"age\":\"20\"},{}]}"; JsonObject object = new JsonParser().parse(companyJson).getAsJsonObject(); System.out.println("========== Encrypt Value ========="); companyJson = cipherValue(object, true); System.out.println(companyJson); System.out.println("========== Decrypt Value ========="); companyJson = cipherValue(object, false); System.out.println(companyJson); } /** * Encrypt OR Decript Method * @param jsonObject * @param isEncrypt true: encrypt false: decrypt * @return */ private static String cipherValue(JsonObject jsonObject, boolean isEncrypt) { Iterator<Map.Entry<String, JsonElement>> iterator = jsonObject.entrySet().iterator(); Map.Entry<String, JsonElement> entry; while (iterator.hasNext()) { entry = iterator.next(); JsonElement value = entry.getValue(); if (value.isJsonPrimitive()) { try { if (isEncrypt) { entry.setValue(new JsonPrimitive(EncryptSDK.encData(entry.getValue().getAsString()))); } else { entry.setValue(new JsonPrimitive(EncryptSDK.decrypt(entry.getValue().getAsString()))); } } catch (Exception e) {} } else if (value.isJsonObject()) { cipherValue(value.getAsJsonObject(), isEncrypt); } else if (value.isJsonArray()) { JsonArray jsonArray = value.getAsJsonArray(); JsonElement jsonElement; for (int i = 0; i < jsonArray.size(); i++) { jsonElement = jsonArray.get(i); cipherValue(jsonElement.getAsJsonObject(), isEncrypt); } } } return jsonObject.toString(); } }
결과
{"name":"65bd107f32a622fc\u0000\u0000\u0000\u0015¡??\u0015\u0003¾?[??Þ???b??[}\nU(ß\u0005?I;ºjØ\tÐ\rðÞÆøt´?Vi?´c952c26628af4204","employees":[{"name":"65bd107f32a622fc\u0000\u0000\u0000\u000bh\u000e\u001f???\u0015???\b/?¼\n ??°DAx¿¶p?¿¶c952c26628af4204","age":"65bd107f32a622fc\u0000\u0000\u0000\u0002h\u000e\u001f???\u0015???\b/\f?,\u0006¤Kq?\"?^\u0011¸?\u000e?c952c26628af4204","sex":"65bd107f32a622fc\u0000\u0000\u0000\u0001h\u000e\u001f???\u0015???\b/?wH\u001c?þE?\u0011¼?B\u0002\u0007??c952c26628af4204"},{"name":"65bd107f32a622fc\u0000\u0000\u0000\u0005???[e??\tv/6?T\rÐ%G??\u0003Np?k???c952c26628af4204","age":"65bd107f32a622fc\u0000\u0000\u0000\u0002???[e??\tv/6?\u000b?±??\u0002m??,N??gºc952c26628af4204","sex":"65bd107f32a622fc\u0000\u0000\u0000\u0001???[e??\tv/6?wH\u001c?þE?\u0011¼?B\u0002\u0007??c952c26628af4204"},{"name":"65bd107f32a622fc\u0000\u0000\u0000\u0004???[e??\tv/6½¸ª)?3??NR??],.c952c26628af4204","sex":"65bd107f32a622fc\u0000\u0000\u0000\u0001???[e??\tv/6?wH\u001c?þE?\u0011¼?B\u0002\u0007??c952c26628af4204"},{"name":"65bd107f32a622fc\u0000\u0000\u0000\u0004\u0000L?.l6\u0015??pv?½??{?\u0004?\u0017D?÷´T?²?c952c26628af4204","age":"65bd107f32a622fc\u0000\u0000\u0000\u0002\u0000L?.l6\u0015??pv?M$N??L]·??±?qm?c952c26628af4204"},{}]}
========== Decrypt Value =========
{"name":"1004lucifer's Company","employees":[{"name":"1004lucifer","age":"30","sex":"M"},{"name":"vvoei","age":"29","sex":"M"},{"name":"John","sex":"M"},{"name":"Jane","age":"20"},{}]}
Gson 에서의 기본적인 Element는 JsonElement 이다.
JsonElement 가 JSON 값(Value)에 해당이 되며 JSON 값의 종류는 다음과 같다.
1004lucifer
1. Array
- "[]" 과 같은 경우를 말한다.
- Array 에는 Null 과 Object 둘중의 하나의 값이 들어가 있다.
1) Null : []
2) Object: [{"a":"b"},{"c","d"}]
2. Null
- 있는 그대로 Null 이다.
- {} <= 이것은 Null 이 아니라 값이 비어있는 Object 이다.
3. Object
- "{}" 로 처리되는 구문이 Object 로 취급된다.
4. Primitive
- Object 와 다른 부분은 순수(primitive)값 이라는 부분에서 차이가 있다.
- Boolean, String, Int, Double, Float 등등 일반적으로 사용하는 값들이 해당된다.
- ex) {"a": "aa", "b": "123", "c": "12.3", "d": {}, "e": [{"b": "bb"}]}
JsonElement 를 상속받은 Subclass 로는 다음이 있다.
1. JsonArray
2. JsonNull
3. JsonObject
4. JsonPrimitive
2. JsonNull
3. JsonObject
4. JsonPrimitive
JavaObject 가 없는 상황에서 JSON 값을 편집하려 하면 JsonElement 의 Subclass 들을 활용해서 값을 받아오고 수정해야 한다.
(2위의 2번째 예제와 같이..)
1004lucifer
만일 가져올 값이 미리 정해져 있고 특정 값만 가져온다고 하면 다음의 Method 를 사용해서 값을 가져올 수 있다.
JsonObject
JsonElement | get(String memberName) Returns the member with the specified name. |
JsonArray | getAsJsonArray(String memberName) Convenience method to get the specified member as a JsonArray. |
JsonObject | etAsJsonObject(String memberName) Convenience method to get the specified member as a JsonObject. |
JsonPrimitive | getAsJsonPrimitive(String memberName) Convenience method to get the specified member as a JsonPrimitive element. |
단순한 JSON이라면 상관 없겠지만 Depth가 있는 JSON 이라면 Depth를 따라 찾아야 한다.
1004lucifer
만일 아래의 JSON 에서 bb 의 55555 라는 값을 가지고 오고 싶다면 다음과 같이 사용하면 된다.
{"a": "aa", "b": "123", "c": "12.3", "d": {}, "e": [{"bb": "55555"}]}
TestGsonString.java
import com.google.gson.*; /** * Created by 1004lucifer on 2015-04-08. */ public class TestGsonString { public static void main(String[] argv) { String companyJson= "{\"a\": \"aa\", \"b\": \"123\", \"c\": \"12.3\", \"d\": {}, \"e\": [{\"bb\": \"55555\"}]}"; JsonObject jsonObject = new JsonParser().parse(companyJson).getAsJsonObject(); JsonArray jsonArray = jsonObject.getAsJsonArray("e"); JsonObject jsonObject1 = jsonArray.get(0).getAsJsonObject(); JsonPrimitive jsonPrimitive = jsonObject1.getAsJsonPrimitive("bb"); int value = jsonPrimitive.getAsInt(); System.out.println("value == 55555 is : " + (value == 55555)); } }
결과
이정도만 알아도 Java 에서 JSON 처리를 어느정도 할 수 있지 않을까 싶다.
2015년 4월 6일 월요일
[리뷰][서적] 자바스크립트 성능 이야기 - 웹 개발자라면 꼭 필독해야 할 웹최적화 방법론 서적
NHN은 이렇게 한다! 자바스크립트 성능 이야기 - 박재성 외 지음/위키북스 |
행사때 싸게 팔길래 큰 기대하지 않고 구입을 했다가 최근에서야 읽어봤는데..
왜 이제서야 봤을까 싶을 정도로 내용이 너무 좋다.
책 제목은 '자바스크립트 성능 이야기' 이지만 실질적으로는 Javascript 뿐만 아니라 HTML, CSS 관련하여 어떻게 최적화를 할 수 있는지 잘 나와있다.
(개인적인 느낌으로는 웹최적화 방법론이 나와있는 서적이라고 생각한다.)
목차를 보면 대략적으로 어떤 내용이 나오겠구나 알 수 있긴 하지만..
내용의 깊이가 꽤 있어서 중급 웹 개발자가 보더라도 많이 도움이 될테고 개념적인 설명이 아주 잘 되어 있어서 이해하기 힘든 그런 내용은 없었다.
하지만 예제가 많거나 하지 않아서 아쉬운 사람도 있을 수도 있겠다.
한번 다 읽어보고 정말 괜찮은 내용이구나 느끼기도 했지만
내용을 다 외우고 있는게 아니다보니 시간날때 한번 더 봐야겠다고 생각되는 서적!!
2015년 4월 5일 일요일
[ADSI][ASP] Creating a Windows user account to Sign Up on a website.(web page)
제목: 웹페이지에서 회원가입으로 윈도우 사용자 계정 생성하기.
(어차피 국내에선 이 글이 크게 도움이 될 것 같지 않아서 외국유입률 올리기 위해 제목을 영어로 작성을 했다.)
학생 시절에 Windows Server 를 이용해서 AD(Active Directory) 구축해서 Terminal 서버를 운영한적이 있었는데 이게 사용자들의 계정을 수동으로 일일히 만들어 줘야 하는 단점이 있었다.
사용자 계정을 자주 만들어야 하는건 아니었지만 사용자에게 아이디와 패스워드를 받아야 하는 불편함이 있어서 인터넷으로 회원가입을 하는 방법을 찾던중에 ADSI (Active Directory Service Interfaces) 라는 기술이 있어서 공부해서 만들었던적이 있었다.
학교 졸업당시 해당 작업을 이력서에 기술하고 포트폴리오도 만들어 놨었는데..
이제는 경력도 쌓이고 더 이상 해당 작업을 포트폴리오 로써 사용할 수 없을 것 같아 여기에 기술한다.
기능
- 아래의 페이지에서 회원가입 시 Windows Server의 사용자 계정이 생성된다.
준비
1. Windows Server 가 필요하다.
2. IIS 서비스가 구성되어있어야 한다.
3. Active Directory 서비스가 구성되어있어야 한다.
PS. 아래의 소스를 IIS 에 셋팅된 홈디렉토리에 추가한다.
소스
CreateUserForm.html
<html> <head> <!-- ===== writeer: 1004lucifer ===== --> <title>회원가입 양식 입니다.</title> </head> <body> <h1>회원 가입 양식</h1> <form method="post" action="CreateUserProc.asp"> 아이디 : <input type="text" name="id" size="16"><br> 비밀번호 : <input type="password" name="pass" size="16"><br><br> 성 : <input type="text" name="lastName" size="10"><br> 이름 : <input type="text" name="firstName" size="10"><br> E-mail : <input type="text" name="mail" size="20"><br> 연락처 : <input type="text" name="phone" size="20"><br> 핸드폰 : <input type="text" name="mobilePhone" size="20"><br> <input type="submit" value="가입"> </form> </body> </html>
CreateUserProc.asp
<% '===== writeer: 1004lucifer ===== on error resume next Const ADS_PROPERTY_APPEND = 3 'PutEx를 사용하기 위해 존재 '----------------------- 값을 넘겨 받음 ----------------------------- Dim id, pass, lastName, firstName, mail, phone, mobilePhone id = Request.Form("id") pass = Request.Form("pass") lastName = Request.Form("lastName") firstName = Request.Form("firstName") mail = Request.Form("mail") phone = Request.Form("phone") mobilePhone = Request.Form("phone") fullName = lastName & " " & firstName '------------------------------------------------------------- Domain = "DC=test01,DC=com" '도메인 이름을 지정함 Container = "CN=Users" '사용자가 생성될 컨테이너를 지정함(CN 또는 OU) ADsPath = "LDAP://" & Container & "," & Domain sUser = id set connect = GetObject("LDAP:") set cont = connect.OpenDSObject(ADsPath, "administrator", "password", 1) Set user = cont.Create("user", "cn="&sUser) user.put "sAMAccountName", sUser user.put "displayName" , fullName user.put "homePhone", phone user.put "mail", mail user.put "mobile", mobilePhone user.put "userPrincipalName", sUser user.put "userAccountControl", "544" '(계정 사용안함)옵션 취소하기위해 플래그비트값 변경 user.put "pwdLastSet", "-1" '(다음 로그인할때 반드시 암호번경)옵션 취소 user.SetInfo if (Err.number <> 0) then Response.Write "사용자가 이미 있습니다." Response.End end if user.ChangePassword "", pass user.SetInfo groupADsPath = "LDAP://CN=Users,CN=Builtin,DC=test01,DC=com" '소속될 그룹ADsPath를 지정 userADsPath = "CN=" & sUser & "," & Container & "," & Domain set connect = GetObject("LDAP:") set cont = connect.OpenDSObject(groupADsPath, "administrator", "password", 1) cont.PutEx ADS_PROPERTY_APPEND, "member", Array(userADsPath) cont.SetInfo if (Err.number <> 0) then Response.Write "사용자는 생성되었으나 그룹이 존재하지 않아서 그룹에 포함시키지 못했습니다." Response.End end if %>
ADlogin.asp
<% '===== writeer: 1004lucifer ===== computerName = "win2003server01" '컴퓨터 이름 설정 부분 domainName = "DC=test01,DC=com" '도메인 이름 설정 부분 groupName = "CN=Administrators,CN=Builtin" '그룹 이름 설정 부분 ex) "CN=남자직원,OU=인사과" 등등.. ADsPath = "LDAP://" & computerName & "/" & groupName & "," & domainName logonUser = Request.ServerVariables("LOGON_USER") If logonUser = "" Then Response.Status = "401 Authorization Required" Response.End End If STR_IS_AUTH = UCase(Trim(Request.Cookies("AUTH"))) IsLegal = False '권한 저장 If STR_IS_AUTH <> "TRUE" Then '도메인을 제거한 아이디문자열만 얻는다 aryTemp = Split(logonUser, "\") logonUser = aryTemp(UBound(aryTemp)) Set oAuthADsGroup = GetObject(ADsPath) For Each user In oAuthADsGroup.GetEx("Member") '---아이디 문자열만 얻는다----- user = trim(user) strTemp = split(user, ",") user = strTemp(0) length = len(user) user = right(user, length-3) '------------------------------- If UCase(user) = UCase(logonUser) Then Response.Cookies("AUTH") = "TRUE" IsLegal = True Exit For End If Next Set oAuthADsGroup = Nothing If IsLegal = False Then Response.Redirect "index.asp" '로그인 실패했을때 다른 페이지로 보낸다. End If End If response.write "성공적으로 로그인 되었습니다." %>
PS.
거의 10년전쯤에 만든거 같은데..
마지막의 ADlogin.asp 파일은 왜 만들었던 거였더라??
로그인은 '원격데스크톱(mstsc)' 로 하는거였는데 말이지..
직접 실행하지 않으면 잘 모르겠다;;
2015년 4월 3일 금요일
[리뷰][서적] 아파치 코르도바 - 코르도바(폰갭)에 충실하지만 조금은 아쉬운..
모바일 하이브리드 플랫폼의 중심 아파치 코르도바 - 이병옥 지음/엘비오 |
하이브리드앱을 만들어 보려 아래의 책을 봤는데 나에게 맞지 않아서 코르도바 전문서적 '모바일 하이브리드 플랫폼의 중심 아파치 코르도바' 책을 구입해서 읽어봤다.
링크: [리뷰][서적] 쉽게 배우는 웹앱&하이브리드앱 - 비 개발자에게 추천하고 싶은 서적
Do it! 쉽게 배우는 웹앱 & 하이브리드앱 - 김응석 지음/이지스퍼블리싱 |
장점
1. HTML, CSS, Javascript 의 기초부터 설명하지 않고 책의 전반적인 내용이 코르도바(폰갭)에 맞춰져 있다.
2. 이미지가 많고 설명이 쉽게 되어있다.
단점
1. PPT를 프린트한 내용과 같은 화면구성
2. 약간 레퍼런스적인 내용이 많고 예제도 레퍼런스의 기능을 하나하나 익혀가는 예제도 있긴 하지만 제대로된 하이브리드 앱을 만드는 예제가 없다.
3. 최근에 나온 서적 임에도 불구하고 사용하는 개발툴이 Android Studio 가 아니라 Eclipse 로 되어있다.
Android 공식 사이트에서 Eclipse를 버리고 Android Studio 로 갈아탄지 한참이나 되었는데 기대하고서 구매한 서적이 아직도 이클립스를 사용하는 부분은 조금 아쉬웠다.
그리고 지은이(이병옥)가 개발자출신으로 출판사를 설립 후 두번째 출간된 서적인데..
아직 신생 출판사여서 노하우가 없는지 책의 내용을 보는순간 일반적으로 알고있던 IT서적이 아니라 워드나 PPT의 내용을 그냥 책으로 묶은 느낌!?
각 챕터마다 레퍼런스 기능을 설명해 주고 있는데 각각 예제가 있어 대충 어떤식으로 작동이 되는구나 라는걸 볼 수 있지만 한편으로는 몇페이지가 있어서 일반적인 앱의 느낌이 나는 예제가 없어서 조금 아쉽긴 하다.
하지만 책이 두껍지 않고 가볍게 읽어볼 수 있어서 코르도바가 이런거구나..
하고 알 수 있는 책이다.
처음에 책에 나온대로 셋팅 및 빌드를 하는데 오류가 발생을 한다.
다음과 같이 해결을 했다.
링크: cordova(phonegap) 빌드 시 'ANDROID_HOME is set to a non-existant path' 오류 문제
피드 구독하기:
글
(
Atom
)