CINXE.COM

보이스뉴스 | 동아일보

<!DOCTYPE html> <html lang="ko"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>보이스뉴스 | 동아일보</title> <meta name='description' content="동아일보 보이스뉴스는 키보드 동작을 통해 음성으로 기사를 이용할 수 있는 서비스입니다."> <link rel="stylesheet" href="https://image.donga.com/voicenews/css/style.css"> <script src="https://image.donga.com/donga/js.v.5.0/jquery-3.7.0.min.js"></script> <script type="text/javascript" src="https://image.donga.com/donga/js.v.1.0/GAbuilder.js?t=20240911"></script> <script type="text/javascript"> gabuilder = new GAbuilder('GTM-PCVCHHT') ; let pageviewObj = { 'up_login_status' : 'N' } gabuilder.GAScreen(pageviewObj); </script> <script> $(document).ready(function () { var initAudioSequence = false; //최초 실행 let isAudioEnabled = false; //음성 상태 let pendingShortcutGuide = false; //단축키 안내 예약 상태 $('.btn_opt.volume, .volume_box .btn.volume').on('click', function () { toggleVolumeButtons(); }); //고대비 모드 버튼 토글 //250228 수정 $('.btn_opt.high-cont').on('click', function () { $(this).toggleClass('off').focus(); if ($(this).hasClass('off')) { $(this).html('고대비모드 <span class="ic_btn"></span>'); } else { $(this).html('일반모드 <span class="ic_btn"></span>'); } $('body').toggleClass('dark') }) //줌버튼 let zoomIdx = 1; $('.btn_opt').on('click',function(){ if($(this).hasClass('zoomin')){ zoomIdx += 0.5; if(zoomIdx > 5){ zoomIdx = 5; } if(zoomIdx >= 1.5){ $('.headline_box').css({ 'flex-direction':'column', 'align-items':'flex-start' }) }else{ $('.headline_box').css({ 'flex-direction':'row', 'align-items':'center' }) } document.querySelector('html').style.zoom = zoomIdx; }else if($(this).hasClass('zoomout')){ zoomIdx -= 0.5; if(zoomIdx < 1){ zoomIdx = 1; } if(zoomIdx >= 1.5){ $('.headline_box').css({ 'flex-direction':'column', 'align-items':'flex-start' }) }else{ $('.headline_box').css({ 'flex-direction':'row', 'align-items':'center' }) } document.querySelector('html').style.zoom = zoomIdx; }else{ return false; } }) // 헤드라인 클릭 이벤트 핸들러 수정 $(document).on('click', '.headline', function () { handleArticleContent($(this).parents(".headline_box")); }); function toggleVolumeButtons() { $('.btn_opt.volume, .volume_box .btn.volume').each(function() { $(this).toggleClass('off').focus(); if ($(this).hasClass('off')) { $(this).html('음성끄기 <span class="ic_btn"></span>'); } else { $(this).html('음성켜기 <span class="ic_btn"></span>'); } }); // 헤더 버튼의 상태를 기준으로 음성 활성 상태를 설정 isAudioEnabled = $('.btn_opt.volume').hasClass('off'); if (!isAudioEnabled) { $('#audioPlayer')[0].pause(); pendingShortcutGuide = false; // 음성이 꺼지면 예약된 안내 취소 } // 음성이 처음 활성화 되었을 때(첫 실행) if (isAudioEnabled && !initAudioSequence) { initAudioSequence = true; pendingShortcutGuide = true; // 단축키 안내 예약 설정 playMenuAudio(4); // 알림말 재생 const audioPlayer = document.getElementById('audioPlayer'); audioPlayer.addEventListener('ended', function handler() { audioPlayer.removeEventListener('ended', handler); if (pendingShortcutGuide) { // 중간에 다른 재생이 없었는지 확인 setTimeout(function () { if (pendingShortcutGuide) { // 1초 후에도 여전히 예약 상태인지 재확인 playMenuAudio(3); // 단축키 안내 재생 } }, 1000); } }); } } //키이벤트 function keyEvent() { let newsListLength = $(".news_list_wrap .news_item").length; let newsListLimit = newsListLength - 1; let newsTab = 0; $(document).on('keydown', function (e) { if (e.keyCode == 76) { //250228 수정 return false; } }) $(document).on('click keyup', function (e) { if (e.keyCode === 86) { // v 키 - 음성 켜기/끄기 toggleVolumeButtons(); } //뉴스 리스트 외부 클릭 시 포커스 제거 (L키 제외) if (!$(e.target).parents().hasClass('news_list_wrap') && e.keyCode != 76) { //250228 수정 $('.news_list_wrap').removeClass('is_focus'); $(".article_box").removeClass("is_active"); } let cNewsListIdx = $('.headline_box:focus').parents('.news_item').index(); // L키 - 기사 간 포커스 이동 키 이벤트 수정 if (e.keyCode == 76) { //250228 수정 $('.news_list .news_tab .btn_tab').removeClass('is_focus')//250117 추가 if ($('.news_list_wrap.is_focus').length != 0) { cNewsListIdx++; if (cNewsListIdx > newsListLimit) cNewsListIdx = 0; let headlineBox = $('.news_list_wrap .news_item').eq(cNewsListIdx).find('.headline_box'); handleArticleContent(headlineBox, { withTitle: true, expandContent: false }); } else { $('.news_list_wrap').addClass('is_focus'); handleArticleContent($('.news_list_wrap .news_item').eq(0).find('.headline_box'), { withTitle: true, expandContent: false }); } } // 탭키 - 기사 간 포커스 이동 키 이벤트 수정 if (e.keyCode == 9) { if ($(document.activeElement).closest('.news_list_wrap').length > 0 && $(document.activeElement).hasClass('headline_box')) { e.preventDefault(); $('.news_list_wrap').addClass('is_focus'); cNewsListIdx++; if (cNewsListIdx > newsListLimit) cNewsListIdx = 0; handleArticleContent($(document.activeElement), { withTitle: true, expandContent: false }); } } //up - 첫번째 기사 이동 if (e.keyCode == 38) { $('.news_list .news_tab .btn_tab').removeClass('is_focus')//250117 추가 handleArticleContent($(".news_list_wrap .news_item").eq(0).find(".headline_box"), { withTitle: true, expandContent: false }); } // 키이벤트 내부의 enter 키 이벤트 수정 if (e.keyCode == 13) { if ($('.news_list_wrap.is_focus').length != 0) { handleArticleContent($('.headline_box:focus')); } } //뉴스 탭 외부 클릭 시 is_focus 제거 if (!$(e.target).parents().hasClass('news_tab')) { $('.news_tab, .btn_tab').removeClass('is_focus'); } let initNewsTabIdx = $('.btn_tab.is_active').parent('li').index(); let cNewsTabIdx = $('.btn_tab.is_focus').parent('li').index(); //m - 메뉴 이동 if (e.keyCode == 77) { if ($('.news_tab.is_focus').length != 0) { $('.news_tab li .btn_tab').removeClass('is_focus'); newsTab = cNewsTabIdx; newsTab++; if (newsTab > 2) { newsTab = 0; } $('.news_tab li').eq(newsTab).find('.btn_tab').focus().addClass('is_focus'); playMenuAudio(newsTab); } else { $('.news_tab').addClass('is_focus'); $('.news_tab li').eq(0).find('.btn_tab').focus().addClass('is_focus'); playMenuAudio(0); } } //left - 메뉴간 이동 if (e.keyCode == 37) { if ($('.news_tab.is_focus').length != 0) { $('.news_tab li .btn_tab').removeClass('is_focus'); newsTab = cNewsTabIdx; newsTab--; if (newsTab < 0) { newsTab = 2; } $('.news_tab li').eq(newsTab).find('.btn_tab').focus().addClass('is_focus'); playMenuAudio(newsTab); } else { return false; } } //right - 메뉴간 이동 if (e.keyCode == 39) { if ($('.news_tab.is_focus').length != 0) { $('.news_tab li .btn_tab').removeClass('is_focus'); newsTab = cNewsTabIdx; newsTab++; if (newsTab > 2) { newsTab = 0; } $('.news_tab li').eq(newsTab).find('.btn_tab').focus().addClass('is_focus'); playMenuAudio(newsTab); } else { return false; } } //h - 단축키 안내 듣기 if (e.keyCode == 72) { playMenuAudio(3); } }); } keyEvent(); // 공통 기능을 하나의 함수로 통합 function handleArticleContent(headlineBox, options = {}) { const defaults = { withTitle: false, shouldFocus: true, expandContent: true }; const settings = { ...defaults, ...options }; let articleBox = headlineBox.siblings('.article_box'); if (settings.expandContent) { $(".headline_box, .article_box").removeClass("is_active"); } if (articleBox.find('.text').is(':empty')) { loadArticleContent(articleBox.data('content'), function(response) { let jsonData = JSON.parse(response); articleBox.find('.text').html(jsonData.content); articleBox.attr('data-mp3', jsonData.mp3); if (settings.withTitle) { playAudio(jsonData.mp3, 'title'); } else { playAudio(jsonData.mp3); } if (settings.expandContent) { headlineBox.addClass("is_active"); articleBox.addClass("is_active"); } if (settings.shouldFocus) { settings.expandContent ? articleBox.focus() : headlineBox.focus(); } }); } else { if (articleBox.data('mp3')) { if (settings.withTitle) { playAudio(articleBox.data('mp3'), 'title'); } else { playAudio(articleBox.data('mp3')); } } if (settings.expandContent) { headlineBox.addClass("is_active"); articleBox.addClass("is_active"); } if (settings.shouldFocus) { settings.expandContent ? articleBox.focus() : headlineBox.focus(); } } } function loadArticleContent(contentId, callback) { let requestData = { contentId: contentId }; $.ajax({ url: 'https://voice.donga.com/view', type: 'GET', data: requestData, success: function(response) { callback(response); }, error: function(xhr, status, error) { console.error('콘텐츠 로드 실패:', error); } }); } let currentLoadingAudio = null; let currentContentAudio = null; let loadingInterval = null; function playAudio(mp3Url, o = '') { if (!isAudioEnabled) return; pendingShortcutGuide = false; // 단축키 안내 예약 취소 const audioPlayer = document.getElementById('audioPlayer'); if (!mp3Url) { console.error('MP3 URL이 유효하지 않습니다'); return; } // 기존 오디오 정지 stopCurrentAudio(); // 타이틀 재생인 경우 기존 로직 유지 if (o === 'title') { audioPlayer.src = "https://speak.donga.com/news/" + mp3Url + ".mp3?o=" + o; audioPlayer.play(); return; } // 로딩음 및 본문 콘텐츠 오디오 객체 생성 currentLoadingAudio = new Audio('https://speak.donga.com/staticvoice/000000003_1_20250117133114_20250117133114.mp3'); currentContentAudio = new Audio("https://speak.donga.com/news/" + mp3Url + ".mp3"); let loadingTimer; // 반복 타이머 변수 선언 // 본문 오디오 준비 완료 시 호출될 이벤트 핸들러 function onContentReady() { clearTimeout(loadingInterval); // 로딩음 반복 중지 currentLoadingAudio.pause(); currentLoadingAudio.currentTime = 0; audioPlayer.src = currentContentAudio.src; audioPlayer.play(); currentContentAudio.removeEventListener('canplaythrough', onCanPlay); currentContentAudio.removeEventListener('error', onError); } function onCanPlay() { onCanPlay(); } // 본문 오디오 준비 완료 이벤트 처리 currentContentAudio.addEventListener('canplaythrough', onCanPlay); function onCanPlay() { clearTimeout(loadingInterval); // 반복 타이머 제거 if (currentLoadingAudio) { currentLoadingAudio.pause(); currentLoadingAudio.currentTime = 0; } audioPlayer.src = currentContentAudio.src; audioPlayer.play(); currentContentAudio.removeEventListener('canplaythrough', onCanPlay); } // 본문 오디오 로드 실패 처리 currentContentAudio.addEventListener('error', () => { console.error('오디오 로드 실패 : ' + currentContentAudio.src); clearTimeout(loadingInterval); if (currentLoadingAudio) { currentLoadingAudio.pause(); currentLoadingAudio.currentTime = 0; } }); // 로딩음성 반복 재생 함수 정의 function playLoadingRepeatedly() { if (currentContentAudio.readyState < 4) { currentLoadingAudio.currentTime = 0; currentLoadingAudio.play(); loadingInterval = setTimeout(playLoadingRepeatedly, 10000); // 10초 간격 반복 설정 } } // 본문 오디오 준비 완료 이벤트 처리 함수 정의 function onCanPlay() { clearTimeout(loadingInterval); if (currentLoadingAudio) { currentLoadingAudio.pause(); currentLoadingAudio.currentTime = 0; } audioPlayer.src = currentContentAudio.src; audioPlayer.play(); currentContentAudio.removeEventListener('canplaythrough', onCanPlay); } // 최초 로딩음성 재생 및 반복 시작 playLoadingRepeatedly(); // 콘텐츠 오디오 다운로드 시작 및 이벤트 핸들러 등록 currentContentAudio.addEventListener('canplaythrough', onCanPlay); currentContentAudio.load(); } // 메뉴 오디오 재생 함수 추가 function playMenuAudio(tabIndex) { // 음성이 꺼져있으면 재생하지 않음 if (!isAudioEnabled) return; // 단축키 안내나 알림말이 아닌 다른 메뉴 오디오 재생 시 예약 취소 if (tabIndex !== 3 && tabIndex !== 4) { pendingShortcutGuide = false; } const audioFiles = { 0: '000000001_1_20250116185644_20250116185644', // 주요뉴스 1: '000000001_2_20250116185644_20250116185644', // 오피니언 2: '000000001_3_20250116185644_20250116185644', // 실시간뉴스 3: '000000002_3_20250304144438_20250304144438', // 단축키h 4: '000000003_1_20250304144633_20250304144633' // 알림말 }; const audioPlayer = document.getElementById('audioPlayer'); audioPlayer.src = 'https://speak.donga.com/staticvoice/' + audioFiles[tabIndex] + '.mp3'; audioPlayer.play(); } // 탭 클릭 이벤트 핸들러 $('.news_tab a.btn_tab').on('click', function(e) { e.preventDefault(); $('.news_tab a.btn_tab').removeClass('is_active'); $(this).addClass('is_active'); let tabIndex = $(this).parent().index(); loadData(tabIndex); }); function stopCurrentAudio() { // audioPlayer 정지 및 초기화 const audioPlayer = document.getElementById('audioPlayer'); if (audioPlayer && !audioPlayer.paused) { audioPlayer.pause(); audioPlayer.currentTime = 0; } // 전역 변수인 currentLoadingAudio와 currentContentAudio 정지 및 초기화 if (currentLoadingAudio) { currentLoadingAudio.pause(); currentLoadingAudio.currentTime = 0; currentLoadingAudio = null; } if (currentContentAudio) { currentContentAudio.pause(); currentContentAudio.currentTime = 0; currentContentAudio = null; } } function loadData(m) { stopCurrentAudio(); // 기존 재생 중인 오디오 정지 let requestData = { m: m, p0: "j" }; $.ajax({ url: 'https://voice.donga.com', type: 'POST', data: requestData, dataType: "html", success: function(data) { $('#news_list').html(data); }, error: function(xhr, status, error) { console.error('콘텐츠 로드 실패#2:', error); } }); } // loadData 함수를 전역으로 등록 window.loadData = loadData; }) </script> </head> <body> <!-- Google Tag Manager (noscript) --> <noscript><iframe src='https://www.googletagmanager.com/ns.html?id=GTM-PCVCHHT' height='0' width='0' style='display:none;visibility:hidden'></iframe></noscript> <!-- End Google Tag Manager (noscript) --> <div id="wrap"> <header id="header"> <div class="inner"> <div class="option_box"> <button class="btn_opt volume on0">음성켜기 <span class="ic_btn"></span> </button> <button class="btn_opt high-cont off">고대비모드 <span class="ic_btn"></span> </button> <button class="btn_opt zoomin">확대보기 <span class="ic_btn"></span> </button> <button class="btn_opt zoomout">축소보기 <span class="ic_btn"></span> </button> </div> </div> </header> <div id="container"> <section class="section top"> <div class="inner"> <div class="sec_head"> <!-- 250226 수정 --> <!-- 로고위치변경 --> <h1 class="top_logo"> <a href="https://www.donga.com"></a> </h1> <!-- //로고위치변경 --> <h2 class="page_title">보이스 뉴스</h2> <p class="text_title">보이스뉴스는 키보드 동작을 통해 음성으로 기사를 이용할 수 있는 서비스입니다.</p> <!-- 250226 수정 --> <!-- 음성버튼 추가 --> <div class="volume_box"> <button class="btn volume">음성켜기 <span class="ic_btn"></span> </button> </div> <!-- //음성버튼 추가 --> </div> <div class="content"> <div class="content_box"> <h3 class="sec_title">단축키 안내</h3> <ul class="key_guide"> <li class="guide_item">L 키 : 기사간 이동</li> <li class="guide_item">Enter 키 : 기사내용 읽기</li> <li class="guide_item">&#8593;방향키 : 첫번째 기사 이동</li> <li class="guide_item">M 키 :메뉴로 이동</li> <li class="guide_item">&#8596; 방향키 : 메뉴간 이동</li> <li class="guide_item">V 키 :음성 켜기/끄기</li> <li class="guide_item">H 키 : 단축키 안내듣기</li> </ul> </div> </div> </div> </section> <section class="section news_list"> <div class="inner"> <div class="content"> <ul class="news_tab"> <li> <a href="javascript:void(0);" class="btn_tab is_active" onclick="javascript:loadData(0); return false;">주요뉴스</a> </li> <li> <a href="javascript:void(0);" class="btn_tab" onclick="javascript:loadData(1); return false;">오피니언</a> </li> <li> <a href="javascript:void(0);" class="btn_tab" onclick="javascript:loadData(2); return false;">실시간 뉴스</a> </li> </ul> <ul id="news_list" class="news_list_wrap"><li class="news_item"><div class="headline_box" tabindex="0"><p class="headline">[사설]尹 파면… 법치와 민주주의 상식의 확인이다</p><span class="release">2025년 04월 04일 23:30</span></div><div class="article_box" data-content="131353086_2_20250404"><p class="text"></p></div></li><li class="news_item"><div class="headline_box" tabindex="0"><p class="headline">[사설]위기 속 60일, 韓 겸손한 자세로 관리 책임 다하라</p><span class="release">2025년 04월 04일 23:24</span></div><div class="article_box" data-content="131352990_2_20250404"><p class="text"></p></div></li><li class="news_item"><div class="headline_box" tabindex="0"><p class="headline">[사설]野, “일방적 권한 행사 문제 있다” 헌재 지적 새겨야</p><span class="release">2025년 04월 04일 23:21</span></div><div class="article_box" data-content="131352988_2_20250404"><p class="text"></p></div></li><li class="news_item"><div class="headline_box" tabindex="0"><p class="headline">3년간 나라 뒤흔든 ‘영부인 리스크’ [횡설수설/우경임]</p><span class="release">2025년 04월 04일 23:18</span></div><div class="article_box" data-content="131352942_2_20250404"><p class="text"></p></div></li><li class="news_item"><div class="headline_box" tabindex="0"><p class="headline">尹, 무궁화대훈장 못 받는 첫 대통령…월 1500만원 연금도 날아가</p><span class="release">2025년 04월 04일 16:35</span></div><div class="article_box" data-content="131349988_1_20250404"><p class="text"></p></div></li><li class="news_item"><div class="headline_box" tabindex="0"><p class="headline">이재명, 이르면 8일 당대표 사퇴 후 대선 준비 돌입</p><span class="release">2025년 04월 04일 19:33</span></div><div class="article_box" data-content="131351609_1_20250404"><p class="text"></p></div></li><li class="news_item"><div class="headline_box" tabindex="0"><p class="headline">檢, 김건희 불러 ‘공천개입 의혹’ 조사 방침…‘디올백-도이치’도 재수사하나</p><span class="release">2025년 04월 04일 20:28</span></div><div class="article_box" data-content="131347517_1_20250404"><p class="text"></p></div></li><li class="news_item"><div class="headline_box" tabindex="0"><p class="headline">尹 “대선 꼭 승리하기 바란다”…관저찾은 국힘 지도부에 당부</p><span class="release">2025년 04월 04일 18:16</span></div><div class="article_box" data-content="131351684_1_20250404"><p class="text"></p></div></li><li class="news_item"><div class="headline_box" tabindex="0"><p class="headline">[오늘과 내일/김윤종]대통령 탄핵 선고날, 尹 체포했던 공수처의 자조</p><span class="release">2025년 04월 04일 23:15</span></div><div class="article_box" data-content="131352983_2_20250404"><p class="text"></p></div></li><li class="news_item"><div class="headline_box" tabindex="0"><p class="headline">中, 트럼프에 보복 나섰다…“모든 美수입품에 34% 관세 부과”</p><span class="release">2025년 04월 04일 20:45</span></div><div class="article_box" data-content="131352233_1_20250404"><p class="text"></p></div></li><li class="news_item"><div class="headline_box" tabindex="0"><p class="headline">8:0 파면에 탄핵 반대 집회 해산-취소…헌재 주변 ‘진공상태’ 해제</p><span class="release">2025년 04월 04일 17:33</span></div><div class="article_box" data-content="131350327_1_20250404"><p class="text"></p></div></li><li class="news_item"><div class="headline_box" tabindex="0"><p class="headline">국회, ‘최상목 탄핵안’ 법사위 회부…노종면 “좀 더 신중히 판단”</p><span class="release">2025년 04월 04일 15:29</span></div><div class="article_box" data-content="131349975_2_20250404"><p class="text"></p></div></li><li class="news_item"><div class="headline_box" tabindex="0"><p class="headline">트럼프 상호관세는 협상카드, 美 의도부터 파악하라 [동아시론/신동찬]</p><span class="release">2025년 04월 04일 23:12</span></div><div class="article_box" data-content="131352944_2_20250404"><p class="text"></p></div></li><li class="news_item"><div class="headline_box" tabindex="0"><p class="headline">“‘법치의 수호자’ 각인됐던 尹 탄핵”…외신도 긴급 타전</p><span class="release">2025년 04월 04일 19:23</span></div><div class="article_box" data-content="131349772_1_20250404"><p class="text"></p></div></li><li class="news_item"><div class="headline_box" tabindex="0"><p class="headline">산불-관세로 불안한 물가… 서민경제에 ‘올인’할 때다 [광화문에서/장윤정]</p><span class="release">2025년 04월 04일 23:09</span></div><div class="article_box" data-content="131352946_2_20250404"><p class="text"></p></div></li><li class="news_item"><div class="headline_box" tabindex="0"><p class="headline">최상목 “두달간 국가신인도 사수 주력…추경 이달 국회통과 최선”</p><span class="release">2025년 04월 04일 16:05</span></div><div class="article_box" data-content="131349927_1_20250404"><p class="text"></p></div></li><li class="news_item"><div class="headline_box" tabindex="0"><p class="headline">우원식 “어느 한쪽의 승리 아냐…분열 부추기는 행위 중단을”</p><span class="release">2025년 04월 04일 14:37</span></div><div class="article_box" data-content="131349530_1_20250404"><p class="text"></p></div></li><li class="news_item"><div class="headline_box" tabindex="0"><p class="headline">권영세 “헌재 결정 겸허히 수용…국민께 진심으로 사과”</p><span class="release">2025년 04월 04일 11:52</span></div><div class="article_box" data-content="131348387_1_20250404"><p class="text"></p></div></li><li class="news_item"><div class="headline_box" tabindex="0"><p class="headline">용산 대통령실, 국가원수 상징 봉황기 내려…‘대통령 공석’</p><span class="release">2025년 04월 04일 12:30</span></div><div class="article_box" data-content="131348831_2_20250404"><p class="text"></p></div></li><li class="news_item"><div class="headline_box" tabindex="0"><p class="headline">한동훈 “서로를 비난 말고 더 좋은 대한민국 만들자”</p><span class="release">2025년 04월 04일 15:07</span></div><div class="article_box" data-content="131349863_2_20250404"><p class="text"></p></div></li></ul> </div> </div> </section> <audio id="audioPlayer"></audio> </div> <footer id="footer"> <div class="inner"> <p class="copyright">© dongA All rights reserved</p> </div> </footer> </div> </body> </html> <script>var _GCD = '4'; </script><script src='https://dimg.donga.com/acecounter/acecounter_V70.20130719.js'></script><script>_PL(_rl);</script>

Pages: 1 2 3 4 5 6 7 8 9 10