fix(cors): 인앱 브라우저 CORS 차단 해결 — 카톡 WebView 대응

- new Error() → cb(null, false): 500 에러 대신 CORS 헤더 미포함으로 거부
- *.technicalkorea.net 와일드카드 추가: 서브도메인 간 통신 보장

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Hyungi Ahn
2026-04-01 15:15:49 +09:00
parent 697af50963
commit 48e3b58865
7 changed files with 20 additions and 14 deletions

View File

@@ -30,8 +30,8 @@ if (process.env.NODE_ENV === 'development') {
}
app.use(cors({
origin: function(origin, cb) {
if (!origin || allowedOrigins.includes(origin) || /^http:\/\/(192\.168\.\d+\.\d+|localhost)(:\d+)?$/.test(origin)) return cb(null, true);
cb(new Error('CORS blocked: ' + origin));
if (!origin || allowedOrigins.includes(origin) || /^https?:\/\/[a-z0-9-]+\.technicalkorea\.net$/.test(origin) || /^http:\/\/(192\.168\.\d+\.\d+|localhost)(:\d+)?$/.test(origin)) return cb(null, true);
cb(null, false);
},
credentials: true
}));

View File

@@ -50,6 +50,12 @@ const corsOptions = {
return callback(null, true);
}
// *.technicalkorea.net 서브도메인 허용 (인앱 브라우저 대응)
if (/^https?:\/\/[a-z0-9-]+\.technicalkorea\.net$/.test(origin)) {
logger.debug('CORS: technicalkorea.net 서브도메인 허용', { origin });
return callback(null, true);
}
// 개발 환경에서는 모든 localhost 허용
if (process.env.NODE_ENV === 'development') {
if (origin.includes('localhost') || origin.includes('127.0.0.1')) {
@@ -64,9 +70,9 @@ const corsOptions = {
return callback(null, true);
}
// 차단
// 차단 (500 에러 대신 CORS 헤더 미포함으로 거부)
logger.warn('CORS: 차단된 Origin', { origin });
callback(new Error(`CORS 정책에 의해 차단됨: ${origin}`));
callback(null, false);
},
/**

View File

@@ -29,8 +29,8 @@ if (process.env.NODE_ENV === 'development') {
}
app.use(cors({
origin: function(origin, cb) {
if (!origin || allowedOrigins.includes(origin) || /^http:\/\/192\.168\.\d+\.\d+(:\d+)?$/.test(origin)) return cb(null, true);
cb(new Error('CORS blocked: ' + origin));
if (!origin || allowedOrigins.includes(origin) || /^https?:\/\/[a-z0-9-]+\.technicalkorea\.net$/.test(origin) || /^http:\/\/192\.168\.\d+\.\d+(:\d+)?$/.test(origin)) return cb(null, true);
cb(null, false);
},
credentials: true
}));

View File

@@ -25,8 +25,8 @@ if (process.env.NODE_ENV === 'development') {
}
app.use(cors({
origin: function(origin, cb) {
if (!origin || allowedOrigins.includes(origin) || /^http:\/\/192\.168\.\d+\.\d+(:\d+)?$/.test(origin)) return cb(null, true);
cb(new Error('CORS blocked: ' + origin));
if (!origin || allowedOrigins.includes(origin) || /^https?:\/\/[a-z0-9-]+\.technicalkorea\.net$/.test(origin) || /^http:\/\/192\.168\.\d+\.\d+(:\d+)?$/.test(origin)) return cb(null, true);
cb(null, false);
},
credentials: true
}));

View File

@@ -27,8 +27,8 @@ if (process.env.NODE_ENV === 'development') {
}
app.use(cors({
origin: function(origin, cb) {
if (!origin || allowedOrigins.includes(origin) || /^http:\/\/192\.168\.\d+\.\d+(:\d+)?$/.test(origin)) return cb(null, true);
cb(new Error('CORS blocked: ' + origin));
if (!origin || allowedOrigins.includes(origin) || /^https?:\/\/[a-z0-9-]+\.technicalkorea\.net$/.test(origin) || /^http:\/\/192\.168\.\d+\.\d+(:\d+)?$/.test(origin)) return cb(null, true);
cb(null, false);
},
credentials: true
}));

View File

@@ -23,8 +23,8 @@ if (process.env.NODE_ENV === 'development') {
}
app.use(cors({
origin: function(origin, cb) {
if (!origin || allowedOrigins.includes(origin) || /^http:\/\/192\.168\.\d+\.\d+(:\d+)?$/.test(origin)) return cb(null, true);
cb(new Error('CORS blocked: ' + origin));
if (!origin || allowedOrigins.includes(origin) || /^https?:\/\/[a-z0-9-]+\.technicalkorea\.net$/.test(origin) || /^http:\/\/192\.168\.\d+\.\d+(:\d+)?$/.test(origin)) return cb(null, true);
cb(null, false);
},
credentials: true
}));

View File

@@ -42,8 +42,8 @@ if (process.env.NODE_ENV === 'development') {
}
app.use(cors({
origin: function(origin, cb) {
if (!origin || allowedOrigins.includes(origin) || /^http:\/\/192\.168\.\d+\.\d+(:\d+)?$/.test(origin)) return cb(null, true);
cb(new Error('CORS blocked: ' + origin));
if (!origin || allowedOrigins.includes(origin) || /^https?:\/\/[a-z0-9-]+\.technicalkorea\.net$/.test(origin) || /^http:\/\/192\.168\.\d+\.\d+(:\d+)?$/.test(origin)) return cb(null, true);
cb(null, false);
},
credentials: true
}));