회원 연동

내 사이트에 로그인한 회원의 닉네임·아이디·레벨·권한으로 채팅방에 들어오게 하는 흐름과, 접속이 안 될 때 확인할 것을 설명해요.

마지막 수정 2026년 10월 19일

이 페이지

회원 연동을 쓰면 내 사이트 회원이 사이트의 닉네임·아이디·레벨·권한 그대로 채팅방에 들어와요. 내 서버가 접속 정보에 채팅방 토큰으로 서명하고, 유챗은 서명이 맞는 접속만 받으므로 다른 사람의 닉네임을 흉내 낼 수 없어요.

동작 흐름

  1. 회원이 내 사이트 페이지를 열어요.
  2. 내 서버가 로그인한 회원 정보(nick, id, level, auth 등)와 채팅방 아이디, 현재 시각을 모아 채팅방 토큰으로 서명한 문자열을 만들어요.
  3. 그 문자열을 <u-chat user_data="...">에 넣어 페이지를 내려 보내요.
  4. 채팅 서버가 서명을 확인하고, 맞으면 그 정보로 입장시켜요.

토큰은 서명을 만드는 데만 쓰고 페이지에는 나가지 않아요. 서명 문자열을 만드는 규칙과 필드 전체는 회원 연동 서명 규격이 정본이에요.

켜는 순서

  1. 관리 페이지 회원 연동 메뉴에서 회원 연동을 켜요.
  2. 설치 코드 메뉴에서 사이트 종류(PHP, Node.js, Python, Java, 그누보드 등)를 고르고 코드 만들기를 눌러요. 이 코드에는 채팅방 토큰이 들어 있어요.
  3. 코드의 회원 정보 변수를 내 사이트에 맞게 바꿔 서버 코드에 넣어요.

회원 연동을 켜는 순간부터 서명 없는 접속은 거절돼요(오류 203). 사이트 코드를 먼저 바꿔 둔 뒤 켜는 편이 안전해요. 로그인하지 않은 방문자도 서명해야 해요 → 로그인하지 않은 방문자. 그누보드·워드프레스 같은 CMS는 5분 설치를 보세요.

로그인하지 않은 방문자

회원 연동이 켜진 채팅방은 모든 접속에 서명이 있어야 해요. 로그인하지 않은 방문자에게도 user_data를 넣어야 하고, 빼면 오류 203으로 들어오지 못해요.

  • 닉네임·아이디 없이 채팅방 아이디와 시각만 서명하면 손님으로 들어와요. 손님 닉네임은 자동으로 붙어요.
  • 아래 PHP·Node.js·Python·Java 코드는 회원 정보 변수가 비어 있으면 그대로 손님 서명을 만들어요.
  • 회원 여부에 따라 <u-chat>이나 user_data를 빼는 코드를 따로 짜지 마세요.
// 로그인했으면 회원 정보, 아니면 빈 정보로 서명해요. 손님에게도 user_data를 빼지 않아요.
const userData = uchatUserData(user ? { nick: user.nickname, id: user.id } : {});

PHP

관리 화면이 만들어 주는 PHP 코드예요. $joinData의 회원 정보만 내 사이트 변수로 바꿔요. PHP 5.6 이상에 기본으로 들어 있는 함수(hash_hmac, bin2hex, rawurlencode)만 쓰므로 따로 설치할 것이 없어요.

<?php
if(!function_exists('uchat_array2data')) {
	function uchat_array2data($arr) {
		$arr['time'] = time();
		$token = $arr['token'];
		unset($arr['token']);
		ksort($arr, SORT_STRING);
		$msg = 'uchat-v2';
		$out = array();
		foreach ($arr as $k => $v) {
			$v = (string)$v;
			if ($v === '') continue;
			$msg .= "\n".$k.'='.bin2hex($v);
			$out[] = $k.' '.rawurlencode($v);
		}
		$out[] = 'hash v2.'.hash_hmac('sha256', $msg, $token);
		return implode('|', $out);
	}
}
$joinData = array();
$joinData['room'] = 'my-room';
$joinData['token'] = '채팅방 토큰'; // 절대 공개하지 마세요
$joinData['nick'] = $닉네임변수;
$joinData['id'] = $아이디변수;
$joinData['level'] = $레벨변수;
$joinData['auth'] = ''; // admin, subadmin, member, guest 중 하나. 비워 두면 아이디가 있으면 회원, 없으면 손님
$joinData['icons'] = $아이콘주소변수;
?>
<script async src="https://client.uchat.io/uchat.js"></script>
<u-chat room='<?php echo $joinData['room'];?>' user_data='<?php echo uchat_array2data($joinData); ?>' style="display:inline-block; width:100%; max-width:500px; height:500px;"></u-chat>

사이트 관리자에게만 채팅방 관리자 권한을 주려면 $joinData['auth'] = $is_admin ? 'admin' : '';처럼 써요.

Node.js·Python

관리 화면은 Node.js·Python·Java 코드도 만들어 줘요. 모두 그 언어의 기본 라이브러리만 써요. 아래는 Node.js(18 이상) 코드예요.

// 서버에서만 실행하세요. 토큰을 브라우저로 보내면 안 됩니다.
import { createHmac } from 'node:crypto';

const UCHAT_ROOM = 'my-room';
const UCHAT_TOKEN = '채팅방 토큰'; // 절대 공개하지 마세요

export function uchatUserData(user, now = Math.floor(Date.now() / 1000)) {
	const fields = Object.entries({ ...user, room: UCHAT_ROOM, time: now })
		.filter(([, v]) => v !== undefined && v !== null && v !== '')
		.map(([k, v]) => [k, String(v)])
		.sort(([a], [b]) => (a < b ? -1 : 1));
	const message = ['uchat-v2', ...fields.map(([k, v]) => `${k}=${Buffer.from(v, 'utf8').toString('hex')}`)].join('\n');
	const hash = createHmac('sha256', UCHAT_TOKEN).update(message, 'utf8').digest('hex');
	return fields
		.map(([k, v]) => `${k} ${encodeURIComponent(v)}`)
		.concat(`hash v2.${hash}`)
		.join('|');
}

// 예(Express): uchatUserData({ nick: req.user.nickname, id: req.user.id, level: req.user.level, auth: req.user.isAdmin ? 'admin' : '' })

Python(3.8 이상):

# 서버에서만 실행하세요. 토큰을 브라우저로 보내면 안 됩니다.
import binascii
import hashlib
import hmac
import time
from urllib.parse import quote

UCHAT_ROOM = 'my-room'
UCHAT_TOKEN = '채팅방 토큰'  # 절대 공개하지 마세요


def uchat_user_data(user, now=None):
    data = dict(user, room=UCHAT_ROOM, time=int(time.time() if now is None else now))
    fields = sorted((k, str(v)) for k, v in data.items() if v is not None and str(v) != '')
    message = '\n'.join(['uchat-v2'] + ['%s=%s' % (k, binascii.hexlify(v.encode('utf-8')).decode('ascii')) for k, v in fields])
    digest = hmac.new(UCHAT_TOKEN.encode('utf-8'), message.encode('ascii'), hashlib.sha256).hexdigest()
    parts = ['%s %s' % (k, quote(v, safe="!~*'()")) for k, v in fields]
    return '|'.join(parts + ['hash v2.' + digest])

만든 문자열은 HTML 속성에 넣을 때 템플릿 엔진의 이스케이프를 거쳐 <u-chat room="my-room" user_data="...">로 출력해요. 목록에 없는 언어로 옮길 때는 회원 연동 서명 규격의 확인용 예시 값으로 결과를 맞춰 보세요.

Next.js·Nuxt·SvelteKit

서버 코드가 있는 프레임워크는 서버에서 서명하고, 화면에는 서명 결과만 npm 패키지 컴포넌트로 넘겨요. 토큰은 서버 환경 변수(.env)에 두고 브라우저로 보내지 않아요.

  1. 서버(Next.js 서버 컴포넌트, Nuxt server/api, SvelteKit +page.server.js)에서 위 Node.js 서명 함수로 로그인 회원의 userData를 만들어요.
  2. 화면에서 @u-chat/react·@u-chat/vue·@u-chat/svelte의 UchatRoom(페이지 안) 또는 UchatWidget(떠 있는 위젯)에 userData로 넘겨요.
<!-- +page.svelte: +page.server.js의 load가 서버에서 uchatUserData(...)로 만든 userData를 넘겨요 -->
<script>
	import { UchatRoom } from '@u-chat/svelte';

	let { data } = $props();
</script>

<UchatRoom room="my-room" userData={data.userData} width="100%" height={500} />

설치 코드 화면에서 사이트 종류를 Next.js·Nuxt·SvelteKit으로 고르면 서명 함수·서버 코드·화면 코드를 한 번에 만들어 줘요. 넣는 모양을 떠 있는 위젯으로 고르면 UchatWidget과 위젯 설정이 props로 들어가요.

user_data_url로 불러오기

페이지 HTML을 캐시하거나 정적 파일로 내보내는 사이트는 서명을 HTML에 넣기 어려워요. 이때는 user_data 대신 user_data_url에 내 서버 주소를 넣어요.

<u-chat room="my-room" user_data_url="/uchat/user-data" style="display:inline-block; width:100%; max-width:500px; height:500px;"></u-chat>
  • 채팅방은 접속할 때 이 주소 뒤에 ?room=채팅방아이디를 붙여 <script>로 불러와요. 그래서 주소에 이미 ?가 들어 있으면 안 돼요.
  • 응답은 자바스크립트이고, 실행되면 window.Uchat_cache.token[채팅방아이디]에 서명 문자열을 넣어야 해요.

Express에서 위 uchatUserData로 응답하는 예시예요.

app.get('/uchat/user-data', (req, res) => {
	res.set({
		'Content-Type': 'application/javascript; charset=utf-8',
		'Cache-Control': 'no-store, private',
		'X-Content-Type-Options': 'nosniff'
	});
	// 다른 사이트가 이 주소를 <script>로 불러 회원의 서명을 가져가지 못하게 한다
	if (req.get('Sec-Fetch-Site') === 'cross-site') return res.status(403).send('/* forbidden */');
	const user = req.user ? { nick: req.user.nickname, id: req.user.id, auth: req.user.isAdmin ? 'admin' : '' } : {};
	const room = JSON.stringify(UCHAT_ROOM);
	res.send(
		'window.Uchat_cache=window.Uchat_cache||{};window.Uchat_cache.token=window.Uchat_cache.token||{};' +
			`window.Uchat_cache.token[${room}]=${JSON.stringify(uchatUserData(user))};`
	);
});

응답은 로그인 세션으로 회원을 확인해 요청마다 새로 서명하고, Cache-Control: no-store로 캐시되지 않게 해요. 로그인하지 않았으면 빈 회원 정보로 서명해 손님으로 들어오게 해요. 주의점은 보안 체크리스트에 있어요.

시간 연동

회원 연동 메뉴의 시간 연동(처음엔 켜져 있음)은 오래된 접속 정보를 거절해요. 누군가 페이지의 user_data를 복사해 두었다가 나중에 쓰는 것을 막아요.

  • 서명에 들어간 time(내 서버의 Unix 초)과 유챗 서버 현재 시각의 차이를 기억해 알아서 따라가요. 내 서버 시계가 조금 틀려도 돼요.
  • 새 접속의 차이가 기억한 차이보다 300초(5분) 넘게 과거면 오류 205로 거절해요.
  • 같은 user_data는 처음 쓴 뒤 5분 동안만 받아요. 다시 써도 기억한 차이는 바뀌지 않아요.
  • 표준시간 고정을 켜면 차이를 기억하지 않고 유챗 서버 시각과 바로 비교해요. 내 서버 시계가 정확할 때 쓰세요.
  • 서버를 옮기거나 시계를 뒤로 5분 넘게 바꿨다면 시간 기준 → 초기화를 눌러요.
  • 끄면 time을 검사하지 않아 복사한 user_data를 언제든 다시 쓸 수 있어요.

시간 연동을 켜면 user_data가 든 페이지를 5분 넘게 캐시하면 안 돼요. 캐시가 필요하면 user_data_url을 쓰세요. 이미 들어온 사람은 연결이 잠깐 끊겨도 처음 들어온 뒤 4시간 안에는 유챗이 알아서 다시 접속시켜요.

닉네임 규칙

  • nick은 15자로 자른 뒤 공백(스페이스·탭·줄바꿈)을 모두 지워요. 홍 길동은 홍길동으로 보여요.
  • 비어 있으면 손님 닉네임을 자동으로 붙여요.
  • 같은 닉네임이 이미 접속해 있으면 뒤에 _와 임의 문자를 붙여 바꿔요. 회원을 구분할 때는 닉네임이 아니라 id를 쓰세요.

접속이 안 될 때

오류 원인과 해결
토큰이 누락됐어요. (203) 회원 연동이 켜진 채팅방에 서명 없이 접속했어요. 설치 코드를 연동 코드로 바꾸고, 로그인하지 않은 방문자에게도 서명한 user_data를 넣으세요.
토큰이 변경된 것 같아요. (202) 서명이 맞지 않아요. 토큰과 채팅방 아이디가 맞는지, 값이 있는 항목을 이름순으로 서명했는지, 서명 규격에 없는 항목을 넣지 않았는지, 값을 rawurlencode(또는 encodeURIComponent와 같은 인코딩)로 넣었는지 확인해요.
페이지를 새로고침해 주세요. (205) 시간 연동 기준보다 5분 넘게 오래된 접속 정보이거나, 같은 user_data를 처음 쓴 뒤 5분이 지났어요. 페이지 캐시를 끄고, 서버 시계를 옮겼다면 시간 기준을 초기화하세요.
접속 비밀번호가 필요해요. (701) 접속 비밀번호를 건 채팅방이에요. 부관리자 이상은 비밀번호 없이 들어와요.

관리 페이지 회원 연동 화면 아래 입장 기록에서 최근 14일 동안의 성공·실패 횟수와 최근 실패 50건(시각, 원인, 접속한 IP, 요청에 실려 온 아이디·닉네임, 서명 시각 차이)을 볼 수 있어요. 실패는 모두 횟수에 들어가고, 자세한 기록은 1분에 30건까지만 30일 동안 남아요. 실패 횟수보다 기록이 적은 날에는 일부만 기록됨이 붙어요.

서명이 맞는지 직접 확인하려면 회원 연동 서명 규격의 확인용 예시 값을 내 코드에 넣어 같은 문자열이 나오는지 비교하세요. 오류는 페이지 스크립트의 after.error 이벤트(data.code)로도 받을 수 있어요. → 이벤트

봐도 잘 이해가 되지 않나요? 문의 게시판에 질문을 남겨 주세요.

상호 UCHAT · 대표 김경욱 · 사업자등록번호 141-05-32832
사업장소재지 경기도 파주시 와석순환로 61 · 통신판매업신고번호 2013-경기파주-4485호
대표번호 010-8677-8633 · 이메일 support@uchat.io

ⓒ 2013–2026 UCHAT. All rights reserved.