Refactoring web audio_player

  • 5,385 views
Uploaded on

1'st FRENDS Meetup

1'st FRENDS Meetup

  • Full Name Full Name Comment goes here.
    Are you sure you want to
    Your message goes here
    Be the first to comment
    Be the first to like this
No Downloads

Views

Total Views
5,385
On Slideshare
0
From Embeds
0
Number of Embeds
22

Actions

Shares
Downloads
7
Comments
0
Likes
0

Embeds 0

No embeds

Report content

Flagged as inappropriate Flag as inappropriate
Flag as inappropriate

Select your reason for flagging this presentation as inappropriate.

Cancel
    No notes for slide

Transcript

  • 1. 오디오 플레이어 리팩토링기 - freestrings@gmail.com
  • 2. 웹 기반 오디오 플레이어 . HTML5 Audio 를 이용한 플레이어 .
  • 3. 웹 오디오를 만들 때 주의점 ?
  • 4. 웹이라서 Refresh 금지
  • 5. 다른 특이점 없나요 ?
  • 6. 네 .
  • 7. 그런 오디오 플레이어의 개선 , 검토를 의뢰 받다 !
  • 8. 문제상황을 설명 듣다 .
  • 9. 설명 설명 설명 설명 설명 설명 설명 설명 설명 설명 설명 설명 설명 설명 설명 설명 설명 주로 재생 중 , 다음곡 넘어가기를 여러 번 실행하면 동작이 멈춘 듯 보이다가 실행 .., 재생 중 일시정지를 한 후 , 다시 재생을 하다가 곡 목록에서 다른 곡을 선택하여 실행하면 바로 플레이가 되지 않고 … 한번에 여러 곡을 넘어가려고 위 화살표 버튼을 여러 번 클릭하면 , 동작이 되지 않고 멈춰 있음 .. 한참을 기다리면 , 몇번 째인지 넘어갔던 곡이 실행됨 . 동작 멈춤 현상이 간혹 재현되며 , 멈춤 현상 중 곡목록 닫기를 하거나 다른 액 션 버튼이 동작하지 않음 .., PC 에서보다 모바일에서 발생 빈도가 높은 상황임 . 어느 순간부터 곡이 재생목록에 추가되지 않는다 . 설명 설명 설명 설명 설명 설명 설명 설명 설명 설명 설명 설명 설명 설명 설명 설명 설명
  • 10. 그냥 , " 될 때가 있고 , 안 될 때가 있다 "
  • 11. 개발환경을 공유 받다 .
  • 12. 동일한 개발서버 동일한 디렉토리에 파일을 FTP 로 올리면서 테스트 한다 .
  • 13. 누가 제 파일을 덮어 쓰면 어떻게 하나요 ?
  • 14. " 작업하는 파일이 달라서 그럴 일은 없어요 ."
  • 15. 1. 기반시스템  소스코드관리시스템┃빌드시스템┃버그관리시스템┃테스트관리시 스템과 테스트 자동화툴┃프로젝트관리시스템┃요구사항관리시스템   2. 조직  프로젝트 구성원의 역할 ┃조직체계  3. 개발방법론과 프로세스  소프트웨어 개발방법론┃소프트웨어 개발 프로세스  4. 사람  인재 확보┃문서 작성 기술  5. 문화  동료 리뷰┃연구┃공유┃품질 우선┃규칙 준수┃장기적인 관점으로 보기 
  • 16. • Blocked UI • Heavy requests • Cookie • Polling • Global control flags.
  • 17. Blocked UI // 회원정보 function MemberInfo() { return sendRequest("/getmemberinfo"); }
  • 18. Blocked UI //ajax 리턴값받기 ( 동기 : GET) function sendRequest(url) { var oj = createHttpRequest(); oj.open("GET",url,false); ... oj.send(); return oj.responseText; }
  • 19. Blocked UI • “ 응답을 바로 리턴을 받는다”는 의미는 ?
  • 20. Blocked UI • “ 응답을 바로 리턴을 받는다”는 의미는 ? – 응답을 받기까지 UI 가 다른 작업을 할 수 없다 .
  • 21. Blocked UI • “ 응답을 바로 리턴을 받는다”는 의미는 ? – 응답을 받기까지 UI 가 다른 작업을 할 수 없다 . • 문제는 ?
  • 22. Blocked UI • “ 응답을 바로 리턴을 받는다”는 의미는 ? – 응답을 받기까지 UI 가 다른 작업을 할 수 없다 . • 문제는 ? – 웹 브라우저가 멈춘다 .
  • 23. Heavy requests • 한 곡 재생에 필요한 기본요청 6 개 • 모두 순차적으로 실행
  • 24. Heavy requests
  • 25. Cookie • 쿠키는 사용자 별 정보를 힘들이지 않고 관 리하기에  좋다
  • 26. Cookie • 쿠키는 사용자 별 정보를 힘들이지 않고 관 리하기에  좋다 • 그렇지만 , 모든 HTTP 요청에 붙어 다닌 다
  • 27. Polling • 주기가 짧을 수록 거의 실시간으로 서버와 정보를 동기화 할 수 있다 .
  • 28. Polling • 주기가 짧을 수록 거의 실시간으로 서버와 정보를 동기화 할 수 있다 . • 그렇지만 , 주기가 짧으면 부하가 생긴다 .
  • 29. Polling • 주기가 짧을 수록 거의 실시간으로 서버와 정보를 동기화 할 수 있다 . • 그렇지만 , 주기가 짧으면 부하가 생긴다 . • 음악사이트는 다른 서비스 보다 사이트에 상주하는 시간이 길다 .
  • 30. 기술에 대한 잘못된 이해와 활용은 심 각한 문제다 .
  • 31. 기술에 대한 잘못된 이해와 활용은 심 각한 문제다 . 그렇지만 , 문제 진단만 되면 , 서핑과 시간으로 해결 될 수 있다 .
  • 32. 리펙토링
  • 33. 리펙토링 • " 코드가 작성된 후에 디자인을 개선하는 작업 .“
  • 34. 리펙토링 • " 코드가 작성된 후에 디자인을 개선하는 작업 .“ • 왜 잘 동작하는 코드를 고쳐 ?
  • 35. 리펙토링 • " 코드가 작성된 후에 디자인을 개선하는 작업 .“ • 왜 잘 동작하는 코드를 고쳐 ? • 돌기만 하면 되잖아요 !
  • 36. 리펙토링 • 코드가 작성된 후에 디자인을 개선하는 작업 . • “ 왜 잘 동작하는 코드를 고쳐 ?” • “ 돌기만 하면 되잖아요 !” • “ 일단 대충 이렇게 하시죠”
  • 37. 소프트웨 개발에서는 , 디자인을 한 다음 코딩을 한다 . 좋은 디자인이 먼저다 .
  • 38. 그러나 , 시간이 지나면 코드는 수정되고 ,
  • 39. 그러나 , 시간이 지나면 코드는 수정되고 , 본래의 모습은 형채도 없이 사라진다 .
  • 40. 그러나 , 시간이 지나면 코드는 수정되고 , 본래의 모습은 형채도 없이 사라진다 . 코드는 엔지니어링에서 해킹으로 변질된다
  • 41. 좋은 디자인은 지속적으로 좋은 디자인을 찾는 습관부터다
  • 42. HTML5 Audio Element • http://www.w3.org/wiki/HTML/Elements/audio
  • 43. <audio id="myAudio"></audio> <script> function aud_play_pause() { var myAudio = document.getElementById("myAudio"); if (myAudio.paused) { myAudio.play(); } else { myAudio.pause(); } } </script>
  • 44. 쉽다 .
  • 45. <audio id='Player' ...></audio> <button .. onclick="Play()" > 재생 </button> function Play() { … Player.play(); ... } 실제코드 1
  • 46. Player = document.getElementById('Player'); ... Player.addEventListener("playing", OnPlaying , true); ... function OnPlaying() { // 재생로그 ( 시작 ) if (StartPlayLogFlag == false) { PlayerLog("S", SongIDS[PlayIndex], 1, PlayLogInfo); StartPlayLogFlag = true; } } 실제코드 2
  • 47. var SongIDS = new Array(); // 곡아이디 ( 배열 ) var SongInfos = new Array(); // 곡정보 ( 배열 ) var PlayIndex = -1; // 플레이순서 var PlaySongID = ""; // 플레이곡 var LoopFlag = false; // 전체반복여부 var OneLoopFlag = false; // 한곡반복여부 var ShuffleFlag = false; // 셔플여부 var PauseFlag = false; // 일시중지여부 var LibrarySongFlag = false; // 좋아요여부 var MemberStreamingFlag = false; // 회원스트리밍이용권여부 ... var PlayerLoginLayerFlag = false; // 로그인레이어표시여부 var PlayerBuyItemLayerFlag = false; // 구매레이어표시여부 var StartPlayLogFlag = false; // 시작로그여부 var MiddlePlayLogFlag = false; // 중간로그여부 var EndPlayLogFlag = false; // 종료로그여부 var PlayLogInfo = ""; // 로그정보 var ChannelCode = ""; // 채널코드 var MaxCount = 200; // 최대수 실제코드 3
  • 48. function SongPlay(songID) { // 곡재생 ... 조건문의 많은 코드 조건문의 많은 코드 조건문의 많은 코드 조건문의 많은 코드 조건문의 많은 코드 조건문의 많은 코드 조건문의 많은 코드 조건문의 많은 코드 조건문의 많은 코드 조건문의 많은 코드 조건문의 많은 코드 조건문의 많은 코드 ... } 실제코드 4
  • 49. function Loop(flag) { ... if (flag == "one") { document.getElementById("btnLoop1_1").style.display = "none; document.getElementById("btnLoop1_2").style.display = "none" document.getElementById("btnLoop2_1").style.display = "" document.getElementById("btnLoop2_2").style.display = "" document.getElementById("btnLoop3_1").style.display = "none" document.getElementById("btnLoop3_2").style.display = "none" OneLoopFlag = true; LoopFlag = false; } if (flag == "all") {...} if (flag == "") {...} ... } 실제코드 5
  • 50. function Prev () { if(SongIDS.length > 0) { PauseFlag = false; if (NextFunction != null) clearInterval(NextFunction); if (ShuffleFlag == true) { PlayIndex = Math.floor( SongIDS.length * Math.random() ); } else { PlayIndex = PlayIndex - 1; if (PlayIndex < 0) { // 이전곡이 없습니다 . 레이어표시 ... } Play(); } } } 실제코드 6
  • 51. 다들 이렇게 코딩하지 않음 ? 뭐가 문제일까요 ?
  • 52. <audio id='Player' ...></audio> <button .. onclick="Play()" > 재생 </button> function Play() { … Player.play(); ... } 실제코드 1
  • 53. Player = document.getElementById('Player'); ... Player.addEventListener("playing", OnPlaying , true); ... function OnPlaying() { // 재생로그 ( 시작 ) if (StartPlayLogFlag == false) { PlayerLog("S", SongIDS[PlayIndex], 1, PlayLogInfo); StartPlayLogFlag = true; } } 실제코드 2
  • 54. var SongIDS = new Array(); // 곡아이디 ( 배열 ) var SongInfos = new Array(); // 곡정보 ( 배열 ) var PlayIndex = -1; // 플레이순서 var PlaySongID = ""; // 플레이곡 var LoopFlag = false; // 전체반복여부 var OneLoopFlag = false; // 한곡반복여부 var ShuffleFlag = false; // 셔플여부 var PauseFlag = false; // 일시중지여부 var LibrarySongFlag = false; // 좋아요여부 var MemberStreamingFlag = false; // 회원스트리밍이용권여부 ... var PlayerLoginLayerFlag = false; // 로그인레이어표시여부 var PlayerBuyItemLayerFlag = false; // 구매레이어표시여부 var StartPlayLogFlag = false; // 시작로그여부 var MiddlePlayLogFlag = false; // 중간로그여부 var EndPlayLogFlag = false; // 종료로그여부 var PlayLogInfo = ""; // 로그정보 var ChannelCode = ""; // 채널코드 var MaxCount = 200; // 최대수 실제코드 3
  • 55. function SongPlay(songID) { // 곡재생 ... 조건문의 많은 코드 조건문의 많은 코드 조건문의 많은 코드 조건문의 많은 코드 조건문의 많은 코드 조건문의 많은 코드 조건문의 많은 코드 조건문의 많은 코드 조건문의 많은 코드 조건문의 많은 코드 조건문의 많은 코드 조건문의 많은 코드 ... } 실제코드 4
  • 56. function Loop(flag) { ... if (flag == "one") { document.getElementById("btnLoop1_1").style.display = "none; document.getElementById("btnLoop1_2").style.display = "none" document.getElementById("btnLoop2_1").style.display = "" document.getElementById("btnLoop2_2").style.display = "" document.getElementById("btnLoop3_1").style.display = "none" document.getElementById("btnLoop3_2").style.display = "none" OneLoopFlag = true; LoopFlag = false; } if (flag == "all") {...} if (flag == "") {...} ... } 실제코드 5
  • 57. function Prev () { if(SongIDS.length > 0) { PauseFlag = false; if (NextFunction != null) clearInterval(NextFunction); if (ShuffleFlag == true) { PlayIndex = Math.floor( SongIDS.length * Math.random() ); } else { PlayIndex = PlayIndex - 1; if (PlayIndex < 0) { // 이전곡이 없습니다 . 레이어표시 ... } Play(); } } } 실제코드 6
  • 58. " 컨트롤 변수의 조건에 따라 분기가 되고 , 객체 / 변수의 접근을 제약 할 수 없다 ." 정도를 공통점으로 꼽을 수 있다 .
  • 59. 왜 이렇게 되었을 까요 ?
  • 60. 왜 이렇게 되었을 까요 ? • 익숙하지 않은 기술
  • 61. 왜 이렇게 되었을 까요 ? • 익숙하지 않은 기술 • 기획자에게 질문 할 수록 불어나는 요구사항
  • 62. 왜 이렇게 되었을 까요 ? • 익숙하지 않은 기술 • 기획자에게 질문 할 수록 불어나는 요구사항 • 촉박한 일정
  • 63. 왜 이렇게 되었을 까요 ? • 익숙하지 않은 기술 • 기획자에게 질문 할 수록 불어나는 요구사항 • 촉박한 일정 • 어제도 야근
  • 64. 왜 이렇게 되었을 까요 ? • 익숙하지 않은 기술 • 기획자에게 질문 할 수록 불어나는 요구사항 • 촉박한 일정 • 어제도 야근 • 나도 예전에 개발 좀 했다고 운떼는 PM/ 고참
  • 65. 잠시 , 조건문의 단순화에 대한 학구모드
  • 66. 잠시 , 조건문의 단순화에 대한 학구모드 • 객체지향에서 조건은 다형성으로 상당 부 처리가 될 수 있다 .
  • 67. 잠시 , 조건문의 단순화에 대한 학구모드 • 객체지향에서 조건은 다형성으로 상당 부 처리가 될 수 있다 . – 예를 들면 , Visitor 패턴
  • 68. http://java.dzone.com/articles/design-patterns-visitor
  • 69. http://java.dzone.com/articles/design-patterns-visitor
  • 70. http://java.dzone.com/articles/design-patterns-visitor
  • 71. http://java.dzone.com/articles/design-patterns-visitor
  • 72. http://java.dzone.com/articles/design-patterns-visitor
  • 73. 잠시 , 조건문의 단순화에 대한 학구모드 • 객체지향에서 조건은 다형성으로 상당 부 처리가 될 수 있다 . – 예를 들면 , Visitor 패턴 • 그럼 문제 해결을 위해 객체지향적인 ? 코드로 바꿔야 하나 ?
  • 74. 잠시 , 조건문의 단순화에 대한 학구모드 • 객체지향에서 조건은 다형성으로 상당 부 처리가 될 수 있다 . – 예를 들면 , Visitor 패턴 • 그럼 문제 해결을 위해 객체지향적인 ? 코드로 바꿔야 하나 ? • 자바스크립트는 Overloading 을 지원하지 않는 다 .
  • 75. 조건문의 단순화를 위한 다른 몇가지 기법
  • 76. 조건문의 단순화를 위한 다른 몇가지 기법 • Decompose conditional • Replace Nested Conditional With Guard Clauses • Remove Control Flag
  • 77. Decompose conditional 복잡한 조건문이 있는 경우 http://www.refactoring.com/
  • 78. Replace Nested Conditional With Guard Clauses(1) 메소드가 정상적인 실행 경로를 불명확하게 하는 조건 동작을 가지고 있 는 경우 . http://www.refactoring.com/
  • 79. Replace Nested Conditional With Guard Clauses(2) 메소드가 정상적인 실행 경로를 불명확하게 하는 조건 동작을 가지고 있 는 경우 . http://www.refactoring.com/
  • 80. Remove Control Flag(1) 일련의 boolean 식에서 컨트롤 플래그 역활을 하는 변수가 있는 경우 , break 또는 return 을 사용 http://www.refactoring.com/
  • 81. Remove Control Flag(2) 일련의 boolean 식에서 컨트롤 플래그 역활을 하는 변수가 있는 경우 , break 또는 return 을 사용 http://www.refactoring.com/
  • 82. 학습은 끝났는데 ,, 응용은 ?
  • 83. 학습은 학습일 뿐 .. 노가다로 하나씩 정리 해 보자 .
  • 84. 직접적인 접근 차단 .
  • 85. 직접적인 접근 차단 . • 자바스크립트에서 직접적인 접근 차단은 ?
  • 86. 직접적인 접근 차단 . • 자바스크립트에서 직접적인 접근 차단은 ? • function outer() { function inner() {} }
  • 87. 직접적인 접근 차단 . • 자바스크립트에서 직접적인 접근 차단은 ? • function outer() { function inner() {} } • inner 를 호출 하는 방법은 ?
  • 88. 직접적인 접근 차단 . • 자바스크립트에서 직접적인 접근 차단은 ? • function outer() { function inner() {} } • inner 를 호출 하는 방법은 ? • new Audio() 는 뭔가요 ?
  • 89. 직접적인 접근 차단 . • 자바스크립트에서 직접적인 접근 차단은 ? • function outer() { function inner() {} } • inner 를 호출 하는 방법은 ? • new Audio() 는 뭔가요 ? – 일반적인 DOM Element 는 document 를 통해 서 생성하는 것과 달리 직접 생성이 가능하다 .
  • 90. 잠시 모바일 환경에서 제약사항을 알아보자 iOS Safari 에서는 User action 이 없으면 음악을 자동 재생 할 수 없다 . http://developer.apple.com/library/safari/#documentation/AudioVideo/Conceptual/Using_HTML
  • 91. 그럼 ,, 다른 모바일 OS 의 브라우저 는 자동재생이 된나요 ?
  • 92. 아니 .
  • 93. 그러면 왜 iOS 만 언급을 ?
  • 94. 그러면 왜 iOS 만 언급을 ? iOS 이외엔 browser Audio 를 잘 지원하지 않는다 . 정확히는 모바일류에서 iOS 이외에는 HTML5 Audio 는 서비스에 쓸만하지 않다 .
  • 95. 예를 들면 , 안드로이드에서는 화면을 끄면 음악도 같이 꺼진다 . 홈버튼을 눌러 브라우저가 내려가는 순간 음악이 중지된다 . 물론 , 최신 버전 기준이다 .
  • 96. 그래서 예제에는 빠져있지만 , iOS 에서는 Audio 객체를 활성화시키는 다음과 같은 류의 코드가 필요하다 .
  • 97. 다시 Player 정리로 돌아와서 , 이제 audio 객체에 직접 접근이 불가능 하다 . 뭐가 달라졌나 ?
  • 98. 앞에서 본 코드 1
  • 99. 앞에서 본 코드 1 • 오디오 상태 관리가 필요없다 . – 매번 Player.status() 를 통해 얻는다 . • Audio 의 함수를 직접 호출 해서 발생되는 side effect 도 없다 . – 실제 Audio 객체가 의도하지 않은 곳에서 직 접 호출되고 그 여파로 , 제어 플래그들의 값이 올바르지 않게 되고 있었다 .
  • 100. 앞에서 본 코드 2 • 여러 기능을 가진 Prev() 를 기억하는가 ?
  • 101. 앞에서 본 코드 2 • 여러 기능을 가진 Prev() 를 기억하는가 ? – 재생목록의 기능이 계속 추가되면서 걸레가 된 것이 분명 !
  • 102. 앞에서 본 코드 2 • 여러 기능을 가진 Prev() 를 기억하는가 ? – 재생목록의 기능이 계속 추가되면서 코드가 길 어 진 것이 분명하다 . • 재생목록은 크게 3 가지 기능을 가진다 . – 무작위재생과 순차재생 – 반복모드 – 재생목록의 변경에 따른 기타처리
  • 103. 셔플재생과 일반재생 • 단지 재생 순서 차이다 .
  • 104. 셔플재생과 일반재생 • 단지 재생 순서 차이다 . • Iterator 는 어떨까 ? – hasNext() – next()
  • 105. 셔플재생과 일반재생 • 단지 재생 순서 차이다 . • Iterator 는 어떨까 ? – hasNext() – next() • OrderedIterator vs ShuffleIterator
  • 106. 또 뭐가 달라졌나 ?
  • 107. PlayIndex, PlaySongID, LoopFlag, OneLoopFlag, ShuffleFlag, SongInfos, SongIDS.. 등의 속성이나 컨트롤 플래그들이 사라지거나 숨겨졌다 .
  • 108. 그럼 , " 이전곡이 없습니다 ." 같 은 UI 표현은 어떻게 하나요 ?
  • 109. function Loop(flag) { ... if (flag == "one") { document.getElementById("btnLoop1_1").style.display = "none; document.getElementById("btnLoop1_2").style.display = "none" document.getElementById("btnLoop2_1").style.display = "" document.getElementById("btnLoop2_2").style.display = "" document.getElementById("btnLoop3_1").style.display = "none" document.getElementById("btnLoop3_2").style.display = "none" OneLoopFlag = true; LoopFlag = false; } if (flag == "all") {...} if (flag == "") {...} ... } 앞에서 본 코드 3
  • 110. CSS 에 관련된 개발문제들 • 웹의 장점 중 하나는 CSS 를 통한 유연한 HTML Layout, 그러나 갈수록 복잡해지는 UI • 더없이 높아진 고객 눈높이 – UI 요건 변경이 자주 생김 • JavaScript 에서는 UI 변경을 위해 CSS 클래스 만 추가 / 삭제 하는 것이 추세 • 커스터마이징 하기가 힘든 ExtJS 를 아세요 ? • 디자이너나 퍼블리셔와 개발자간 협업은 서로에 게 영향이 적어야 한다
  • 111. 앞에서 본 코드 4 function SongPlay(songID) { // 곡재생 ... 조건문의 많은 코드 조건문의 많은 코드 조건문의 많은 코드 조건문의 많은 코드 조건문의 많은 코드 조건문의 많은 코드 조건문의 많은 코드 조건문의 많은 코드 조건문의 많은 코드 조건문의 많은 코드 조건문의 많은 코드 조건문의 많은 코드 ... }
  • 112. Pull Up Method • 동일한 일을 하는 메소드를 여러 서브클래 스에서 가지고 있다면 , 이 메소드를 수퍼 클래스로 옮겨라 .
  • 113. Pull Up Method • 동일한 일을 하는 메소드를 여러 서브클래 스에서 가지고 있다면 , 이 메소드를 수퍼 클래스로 옮겨라 . • “ 조건문 많은 코드”인데 갑자기 수퍼클래 스는 무엇인가요 ?
  • 114. Pull Up Method • 동일한 일을 하는 메소드를 여러 서브클래 스에서 가지고 있다면 , 이 메소드를 수퍼 클래스로 옮겨라 . • “ 조건문 많은 코드”인데 갑자기 수퍼클래 스는 뭔가요 ? • 모두 곡 재생에 대한 권한 처리 들이다 . – 이용권 , 19 세 , 서비스 불가곡 , XXX 차감 , 기 타 .
  • 115. Pull Up Method • 동일한 일을 하는 메소드를 여러 서브클래 스에서 가지고 있다면 , 이 메소드를 수퍼 클래스로 옮겨라 . • “ 조건문 많은 코드”인데 갑자기 수퍼클래 스는 뭔가요 ? • 모두 곡 재생에 대한 권한 처리 들이다 . – 이용권 , 19 세 , 서비스 불가곡 , XXX 차감 , 기 타 . • checkAutority(memberInfo, songInfo) 정도 면 공통점이 된다
  • 116. Task 는 이전 Task 의 결과에 따라   실행여부가 결정된다 . 결과가 false 이면 다음 task 를 실행하지 않는다 . 간단해 졌다 .
  • 117. 기술에 대한 잘못된 이해와 활용은 심 각한 문제다 . 그렇지만 , 문제 진단만 되면 , 서핑과 시간으로 해결 될 수 있다 .
  • 118. 문제 진단 능력은 경력과 비례한다 . 코드의 디자인 능력은 노력에 비례한다 . 틈틈이 코드를 정리하는 습관을 지닙시다 .
  • 119. 감사합니다 .
  • 120. 여담 • UI 를 조작하는 부분의 예시가 부족합니다 .
  • 121. 여담 • UI 를 조작하는 부분의 예시가 부족합니다 . – 저도 소위 , ‘ 날코딩’을 했던 터라 뺐습니다 .
  • 122. 여담 • UI 를 조작하는 부분의 예시가 없네요 ? – 저도 소위 , ‘ 날코딩’을 했던 터라 뺐습니다 .
  • 123. 여담 UI 를 조작하는 부분의 예시가 없네요 ? – 저도 소위 , ‘ 날코딩’을 했던 터라 뺐습니다 . • UI 날코딩은 대책이 없나요 ?
  • 124. 여담 • UI 를 조작하는 부분의 예시가 없네요 ? – 저도 날코딩을 했던 터라 뺐습니다 . • UI 날코딩은 대책이 없나요 ? – 잘 구조화된 CSS – 템플릿 엔진 또는 프레임워크
  • 125. 여담 • UI 를 조작하는 부분의 예시가 없네요 ? – 저도 날코딩을 했던 터라 뺐습니다 .. • UI 날코딩은 대책이 없나요 ? – 잘 구조화된 CSS – 템플릿 엔진 또는 프레임워크 • 왜 사용 하지 않았나요 ?
  • 126. 여담 • UI 를 조작하는 부분의 예시가 없네요 ? – 저도 날코딩을 했던 터라 뺐습니다 . • UI 날코딩은 대책이 없나요 ? – 잘 구조화된 CSS – 템플릿 엔진 또는 프레임워크 • 왜 사용 하지 않았나요 ? – 187 request, – 1.7 MB transferred, – 5.8 s ( onload 3.08s, DOMContentLoaded 2.07s) – 이미 페이지 로딩이 너무 느려서 추가 라이브러리를 사 용하기엔 다소 무리가 있었습니다 .