레거시 스킨 API
예전 채팅방이 쓰는 레거시 스킨(basic)의 room.skin API — jQuery DOM, 메뉴바, 팝업, 닉네임 메뉴 — 목록과 기본 스킨으로 옮기는 대응표예요.
마지막 수정 2026년 10월 17일
레거시 스킨(basic, 관리 페이지에는 "레거시 스킨 (구버전)"으로 보임)은 예전 채팅방이 쓰는 스킨이에요. 새 채팅방은 기본 스킨(basic-v2)으로 만들어지며, 새 기능은 기본 스킨에만 더해져요. 이 문서는 아직 레거시 스킨을 쓰는 채팅방을 위한 room.skin 목록이에요. 기본 스킨의 API는 스킨 UI API를 보세요.
페이지 스크립트의 U.chat(), 이벤트, room.action, 채널(room.plugin), 서버 API는 두 스킨에서 같아요. 다른 것은 room.skin뿐이에요.
어느 스킨인지 확인
room.skin의 멤버는 스킨이 화면을 만든 뒤에 생기므로 after.create 안에서 확인해요.
U.chat('my-room').on('after.create', function (room) {
if (room.skin.menubar) {
// 레거시 스킨
} else if (room.skin.menu) {
// 기본 스킨
}
});
userMenu는 두 스킨에 모두 있으므로 구별에 쓰지 마세요. 스킨은 관리 페이지 스킨 메뉴나 <u-chat skin="basic-v2"> 속성으로 바꿀 수 있어요. 회원 연동처럼 스킨과 상관없는 기능은 바꿔도 그대로 동작해요.
원칙
- 레거시 스킨은 채팅방 iframe 안의 DOM을 jQuery(1.12)로 직접 다뤄요. 스킨 HTML 구조와 클래스 이름은 정해진 계약이 아니며, 고친 화면은 스킨이 바뀌면 깨질 수 있어요.
- 등록은 모두
after.create안에서 해요. 그 전에는room.skin.arguments와room.skin.messages만 있어요. - 유챗 로고와 정보 표시를 가리거나 지우지 마세요.
room.skin 목록
| 이름 | 설명 |
|---|---|
room.skin.arguments |
스킨 설정값(관리 페이지 스킨 설정 + skin_arguments 속성 + 전역 Uchat_argument). 스킨이 색 기본값을 채워 넣어요 |
room.skin.messages |
스킨 화면 문구(현재 언어) |
room.skin.window |
채팅방 iframe의 window. jQuery는 room.skin.window.$ |
room.skin.document |
채팅방 iframe의 document |
room.skin.dom |
주요 영역의 jQuery 객체. 아래 dom |
room.skin.menubar |
입력창 아래 메뉴바. 아래 menubar |
room.skin.popup |
팝업(알림·확인·입력·선택). 아래 popup |
room.skin.userMenu |
닉네임을 누르면 나오는 메뉴. 아래 userMenu |
room.skin.scrollDown(force) |
대화를 맨 아래로 내려요. force가 참이 아니면 사용자가 위로 올려 둔 동안에는 내리지 않아요 |
dom
모두 채팅방 iframe 안 요소의 jQuery 객체예요.
| 키 | 영역 |
|---|---|
wrap |
채팅방 전체(.wrap) |
top |
위쪽 제목 영역(.top) |
middle |
대화와 사용자 목록 영역(.middle) |
content |
대화 스크롤 영역(.contentWrap) |
content_target |
대화 줄이 붙는 요소(.content) |
userList |
사용자 목록 스크롤 영역(.userListWrap) |
contour |
사용자 목록과 대화 사이 구분선(.contour) |
bottom |
아래 입력 영역(.bottom) |
inputContent |
입력창(.inputContent) |
menubar |
메뉴바(.menuBar) |
latest_chat |
위로 스크롤했을 때 보이는 최근 대화 알림(.latest_chat) |
setting, settingBG |
설정 창과 그 배경(.settingContent, .settingBackground) |
U.chat('my-room').on('after.create', function (room) {
var $ = room.skin.window.$;
var banner = $('<div/>').text('오늘 8시에 이벤트가 있습니다!').css({ padding: '6px', background: '#fff4cc' });
room.skin.dom.middle.before(banner);
$(room.skin.window).resize(); // 높이가 바뀌었으면 스킨이 다시 배치하게
});
요소를 더해 높이가 바뀌면 $(room.skin.window).resize()를 불러 스킨이 대화 영역 크기를 다시 계산하게 해요. 사용자 입력은 .text()로 넣고 .html()에 넣지 마세요.
menubar
입력창 아래 메뉴바에 버튼을 더해요.
U.chat('my-room').on('after.create', function (room) {
room.skin.menubar.add({
id: 'hello',
title: '인사 보내기',
html: '<div>인사</div>',
condition: function (room, item) {
return room.my.auth >= 1; // false면 보이지 않음
},
onClick: function (room, item) {
room.action.send('안녕하세요!');
},
});
});
| 키 | 설명 |
|---|---|
id |
필수. 겹치면 등록되지 않아요. 버튼에 menubar-아이디 클래스가 붙어요 |
title |
마우스를 올렸을 때 설명 |
html |
버튼 내용(HTML 문자열). 없으면 빈 <div> |
icon |
배경 이미지 주소 |
condition(room, item) |
정확히 false를 돌려주면 그리지 않아요 |
onClick(room, item) |
눌렀을 때. item.event가 클릭 이벤트예요 |
beforeLoad(room, item), afterLoad(room, item) |
그리기 전후. beforeLoad가 false면 그리지 않아요 |
beforeUnload(), afterUnload() |
지우기 전후 |
item은 add에 넘긴 객체 자신이며, 그려진 버튼의 jQuery 객체가 item.layout에 들어가요.
| 함수 | 설명 |
|---|---|
add(item) |
등록해요. 성공하면 true, 아이디가 없거나 겹치면 false |
refresh() |
등록된 버튼을 condition에 따라 그려요 |
remove(id) |
등록을 지워요 |
hide(), show(), toggle() |
메뉴바 전체를 숨기기·보이기·바꾸기 |
list |
등록된 항목 배열 |
add만으로는 화면에 나오지 않아요. 스킨이 입장할 때마다refresh()를 부르므로after.create에서 등록하면 입장할 때 보여요. 입장한 뒤에 등록했다면room.skin.menubar.refresh()를 직접 부르세요.condition은refresh()때만 다시 계산해요. 이미 그려진 버튼의condition이false가 되면 등록이 지워지고, 나중에 다시 참이 되어도 돌아오지 않아요. 권한에 따라 버튼을 숨겼다 보였다 해야 하면condition대신afterLoad에서 받은item.layout을toggle()하세요.remove(id)는 아이디가 숫자가 아니면 그려진 버튼을 화면에서 지우지 못해요. 버튼을 지울 때는item.layout.remove()도 함께 부르세요.- 기본 버튼의 아이디는
scroll,bold,picture,call_admin등이에요.
popup
채팅방 안에 팝업을 띄워요. 팝업은 한 번에 하나만 보이고, 나머지는 순서대로 기다려요. 모든 함수가 팝업 번호를 돌려주며, close(번호)로 닫을 수 있어요. 결과는 Promise가 아니라 콜백으로 받아요.
var popup = room.skin.popup;
popup.confirm('가위바위보', '도전을 받을까요?', function (ok) {
// 확인 true, 취소 false, 바깥을 눌러 닫으면 null
});
popup.prompt('닉네임', '새 닉네임을 적어 주세요.', '기본값', function (value) {
// 입력한 글자, 취소하면 null
});
popup.select('선택', '하나를 고르세요.', { r: '바위', p: '보', s: '가위' }, function (key) {
// 고른 키(문자열), 취소하면 null
});
var id = popup.alert('알림', '10초 뒤에 닫힙니다.');
setTimeout(function () { popup.close(id); }, 10000);
| 함수 | 콜백이 받는 값 |
|---|---|
alert(title, html, onOpen, onClose) |
onOpen(layer, pb, pu)은 열릴 때 불리고, 닫힐 때는 onClose()가 불려요. html은 HTML로 들어가므로 사용자 값은 이스케이프하세요 |
confirm(title, subtitle, fn) |
true, false, 바깥을 누르면 null |
prompt(title, subtitle, value, fn) |
입력한 글자, 취소하면 null |
password(title, subtitle, value, fn) |
prompt와 같고 입력이 가려져요 |
select(title, subtitle, list, fn) |
list(객체 또는 배열)의 키, 취소하면 null |
add(html, type, fn(layer, pb, pu), after) |
직접 만든 팝업. layer는 { close(), centering() }, pb는 배경, pu는 팝업 본체(jQuery). after()는 닫힐 때 |
close([번호]) |
그 팝업을 닫거나 대기열에서 빼요. 번호를 빼면 지금 보이는 팝업을 닫아요 |
closeByType(type) |
add에 준 type이 같은 팝업을 모두 닫아요 |
- 입력 팝업에서 Enter를 누르면 확인과 같아요.
- 브라우저 창이 포커스를 잃으면 지금 보이는 팝업이 바깥을 누른 것처럼 닫혀요(
confirm·prompt·select는null). 다른 창을 열고 돌아오는 흐름에서는 결과가null일 수 있다고 보고 짜세요.
userMenu
닉네임을 누르면 나오는 메뉴에 항목을 더해요. 메뉴는 누를 때마다 새로 만들어지므로 condition이 매번 다시 계산돼요.
room.skin.userMenu.add({
id: 'profile',
text: '프로필 보기',
condition: function (room, data) {
return data.target !== room.my.nick; // 내 닉네임에는 숨김
},
onClick: function (room, data) {
var user = room.user.get(data.target);
if (user && user.id) window.open('/profile/' + encodeURIComponent(user.id));
},
});
| 키 | 설명 |
|---|---|
id |
필수. 겹치면 등록되지 않아요(false) |
text |
항목 글자 |
condition(room, data) |
정확히 false를 돌려주면 숨겨요. data.target은 누른 닉네임 문자열이에요 |
onClick(room, data) |
눌렀을 때. data는 { event, id, target, messageUid } |
| 함수 | 설명 |
|---|---|
add(item) |
항목 하나를 더해요 |
group(left, right) |
두 항목을 한 줄에 나란히 둬요 |
hr([{ condition }]) |
구분선. 아이디는 늘 hr이라 remove('hr')는 구분선을 모두 지워요 |
remove(id) |
그 아이디의 항목을 모두 지워요(기본 항목 포함) |
list |
등록된 항목 배열 |
기본 항목의 아이디는 info, whisper, call, ignore, un_ignore, report, invite, individual, mute, unmute, ban, ip, op, deop, freeze, unfreeze, clearlog, removeChat, logout 등이에요. 기본 항목을 숨기려면 remove(id)를 부르세요. 메뉴를 숨겨도 권한 검사는 서버가 해요.
레거시 전용 이벤트
before.user.list, after.user.list(data는 { list })는 레거시 스킨이 사용자 목록을 열어 전체 목록을 받을 때 와요. 기본 스킨은 사용자 목록을 나눠 받으므로 이 이벤트가 오지 않아요. 나머지 이벤트는 이벤트와 같아요.
기본 스킨으로 옮기기
| 하는 일 | 레거시 스킨 | 기본 스킨 |
|---|---|---|
| 버튼 더하기 | menubar.add({ id, title, html, condition(room, item), onClick(room, item) }) |
menu.add({ id, label, title, condition(ctx), onSelect(ctx) }) |
| 버튼 다시 계산 | menubar.refresh() |
menu.resolver.refresh() |
| 닉네임 메뉴 | userMenu.add({ id, text, onClick(room, data) }), data.target은 닉네임 |
userMenu.add({ id, label, onSelect(ctx) }), ctx.target은 사용자 객체 |
| 기본 항목 숨기기 | userMenu.remove('ignore') |
userMenu.resolver.add(...)로 skin.ignore 걸러 내기 |
| 팝업 | popup.confirm(t, s, fn) 등 콜백 |
dialog.confirm(t, s) 등 Promise |
| 화면에 영역 넣기 | dom.middle.before(...) 등 DOM 직접 |
mount.messageList({ position: 'before', mount(host) }) 등 |
| 대화 위에 겹치기 | dom.content.append(...) + CSS |
mount.overlay({ layer, scope, mount(host) }) |
| 닉네임·메시지 꾸미기 | window.$로 <style> 넣기 |
nickname.resolver, message.resolver |
| 맨 아래로 | scrollDown(force) |
scrollToBottom(force) |
기본 스킨에서는 스킨 DOM, jQuery, <style> 넣기를 지원하지 않아요. 옮긴 뒤의 사용법은 스킨 UI 확장하기부터 보세요.
봐도 잘 이해가 되지 않나요? 문의 게시판에 질문을 남겨 주세요.