Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
14 commits
Select commit Hold shift + click to select a range
a79e094
feature/ dashboardClient controller, service, model 생성, 회원가입기능(apikey…
nemo0824 Mar 10, 2025
7b7f726
feature: dashboardClient 로그인기능
nemo0824 Mar 10, 2025
ee18349
refactor: errorHandle 글로벌 에러핸들러 수정,
nemo0824 Mar 10, 2025
8af0afa
refactor: dashboardClientService(apikey를 bcrypt 에서 sha256으로 변경)
nemo0824 Mar 10, 2025
ccd0ef5
refactor: authenticate(SHA-256으로 hash한것으로 hahsedDomain끼리 조회 후 domain반환 )
nemo0824 Mar 10, 2025
fc53d20
refactor: dashboardClient service(타입단언 수정(get으로 일반객체로 불러오기, ) ), con…
nemo0824 Mar 10, 2025
67ab02d
refactor: dashboardClinet 에러메시지 변경
nemo0824 Mar 10, 2025
6ef8edb
refactor: sdkClientUser apiKeyservice(도메인 조회방법변경(raw 제거, 조회 후 일반객체로 …
nemo0824 Mar 11, 2025
b961fc4
refactor: dashboardClient(apikey 암호화 제거 및 필드에 암호화되지않은 apiKey로 변경)
nemo0824 Mar 11, 2025
0b85761
feature: dashboardClient(로그아웃 기능 추가)
nemo0824 Mar 11, 2025
ee4f480
refactor: dashboardController (도메인 session에서 사용), errorHanle(도메인 없을시 …
nemo0824 Mar 11, 2025
416f11e
refactor: hasDashboardDomain 미들웨어 생성 및 적용
nemo0824 Mar 11, 2025
e0b19b4
refactor: 미들웨어 ensureLogin추가(로그인 했는지 판별)
nemo0824 Mar 11, 2025
83c7f54
refactor: sessionType 추가 , Controller(Request타입 AuthenticatedRequest …
nemo0824 Mar 13, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
99 changes: 17 additions & 82 deletions src/controllers/dashboardController.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,7 @@ import { userPageInfoService } from '../services/userPageInfoService';
export const dashboardController = {
getOnlineUsersCount: async (req: Request, res: Response, next: NextFunction) => {
try {
const domain = req.session.client?.domain;
if (!domain) {
next(new Error('로그인 필요'));
return;
}
const domain = res.locals.dashboardDomain;
const onlineUsersCount = await userConnectionService.getOnlineUsersCount(domain);
res.status(200).json({ onlineUsersCount });
} catch (err) {
Expand All @@ -23,11 +19,7 @@ export const dashboardController = {

getPerPageAverageScrollDepth: async (req: Request, res: Response, next: NextFunction) => {
try {
const domain = req.session.client?.domain;
if (!domain) {
next(new Error('로그인 필요'));
return;
}
const domain = res.locals.dashboardDomain;
const perPageAverageScrollDepth =
await userActionService.getPerPageAverageScrollDepth(domain);
res.status(200).json(perPageAverageScrollDepth);
Expand All @@ -38,11 +30,7 @@ export const dashboardController = {

getPerPageBounceRate: async (req: Request, res: Response, next: NextFunction) => {
try {
const domain = req.session.client?.domain;
if (!domain) {
next(new Error('로그인 필요'));
return;
}
const domain = res.locals.dashboardDomain;
const bounceRate = await userActionService.getPerPageBounceRate(domain);
res.status(200).json(bounceRate);
} catch (err) {
Expand All @@ -52,11 +40,7 @@ export const dashboardController = {

getBrowserStats: async (req: Request, res: Response, next: NextFunction) => {
try {
const domain = req.session.client?.domain;
if (!domain) {
next(new Error('로그인 필요'));
return;
}
const domain = res.locals.dashboardDomain;
const userBrowserStats = await userDeviceService.getBrowserStats(domain);
res.status(200).json(userBrowserStats);
} catch (err) {
Expand All @@ -66,11 +50,7 @@ export const dashboardController = {

getOsStats: async (req: Request, res: Response, next: NextFunction) => {
try {
const domain = req.session.client?.domain;
if (!domain) {
next(new Error('로그인 필요'));
return;
}
const domain = res.locals.dashboardDomain;
const userOsStats = await userDeviceService.getOsStats(domain);
res.status(200).json(userOsStats);
} catch (err) {
Expand All @@ -80,11 +60,7 @@ export const dashboardController = {

getDeviceStats: async (req: Request, res: Response, next: NextFunction) => {
try {
const domain = req.session.client?.domain;
if (!domain) {
next(new Error('로그인 필요'));
return;
}
const domain = res.locals.dashboardDomain;
const userDeviceStats = await userDeviceService.getDeviceStats(domain);
res.status(200).json(userDeviceStats);
} catch (err) {
Expand All @@ -94,11 +70,7 @@ export const dashboardController = {

getResolutionStats: async (req: Request, res: Response, next: NextFunction) => {
try {
const domain = req.session.client?.domain;
if (!domain) {
next(new Error('로그인 필요'));
return;
}
const domain = res.locals.dashboardDomain;
const userResolutionStats = await userDeviceService.getResolutionStats(domain);
res.status(200).json(userResolutionStats);
} catch (err) {
Expand All @@ -108,11 +80,7 @@ export const dashboardController = {

getLanguageStats: async (req: Request, res: Response, next: NextFunction) => {
try {
const domain = req.session.client?.domain;
if (!domain) {
next(new Error('로그인 필요'));
return;
}
const domain = res.locals.dashboardDomain;
const languageStats = await userInfoService.getLanguageStats(domain);
res.status(200).json(languageStats);
} catch (err) {
Expand All @@ -122,11 +90,7 @@ export const dashboardController = {

getCountryStats: async (req: Request, res: Response, next: NextFunction) => {
try {
const domain = req.session.client?.domain;
if (!domain) {
next(new Error('로그인 필요'));
return;
}
const domain = res.locals.dashboardDomain;
const countryStats = await userInfoService.getCountryStats(domain);
res.status(200).json(countryStats);
} catch (err) {
Expand All @@ -136,11 +100,7 @@ export const dashboardController = {

getVisitedUsersRate: async (req: Request, res: Response, next: NextFunction) => {
try {
const domain = req.session.client?.domain;
if (!domain) {
next(new Error('로그인 필요'));
return;
}
const domain = res.locals.dashboardDomain;
const getVisitedUsersRate = await userInfoService.getVisitedUsersRate(domain);
res.status(200).json(getVisitedUsersRate);
} catch (err) {
Expand All @@ -150,11 +110,7 @@ export const dashboardController = {

getReferrerStats: async (req: Request, res: Response, next: NextFunction) => {
try {
const domain = req.session.client?.domain;
if (!domain) {
next(new Error('로그인 필요'));
return;
}
const domain = res.locals.dashboardDomain;
const referrerStatus = await userPageInfoService.getReferrerStats(domain);
res.status(200).json(referrerStatus);
} catch (err) {
Expand All @@ -164,11 +120,7 @@ export const dashboardController = {

getAveragePageLoadTime: async (req: Request, res: Response, next: NextFunction) => {
try {
const domain = req.session.client?.domain;
if (!domain) {
next(new Error('로그인 필요'));
return;
}
const domain = res.locals.dashboardDomain;
const avgLoadTime = await userPageInfoService.getAveragePageLoadTime(domain);
res.status(200).json(avgLoadTime);
} catch (err) {
Expand All @@ -178,11 +130,7 @@ export const dashboardController = {

getPageViewCount: async (req: Request, res: Response, next: NextFunction) => {
try {
const domain = req.session.client?.domain;
if (!domain) {
next(new Error('로그인 필요'));
return;
}
const domain = res.locals.dashboardDomain;
const { startDate, endDate } = req.query;
if (typeof startDate !== 'string' || typeof endDate !== 'string') {
res.status(400).json({ message: '시작 날짜와 종료날짜 올바르게 입력하세요' });
Expand All @@ -201,11 +149,7 @@ export const dashboardController = {

getVisitorsByPeriod: async (req: Request, res: Response, next: NextFunction) => {
try {
const domain = req.session.client?.domain;
if (!domain) {
next(new Error('로그인 필요'));
return;
}
const domain = res.locals.dashboardDomain;
const { startDate, endDate } = req.query;
if (typeof startDate !== 'string' || typeof endDate !== 'string') {
res.status(400).json({ message: 'startDate와 endDate는 필수입니다.' });
Expand All @@ -224,11 +168,7 @@ export const dashboardController = {

getTotalVisitors: async (req: Request, res: Response, next: NextFunction) => {
try {
const domain = req.session.client?.domain;
if (!domain) {
next(new Error('로그인 필요'));
return;
}
const domain = res.locals.dashboardDomain;
const totalVisitorsData = await userPageInfoService.getTotalVisitors(domain);
res.status(200).json(totalVisitorsData);
} catch (err) {
Expand All @@ -238,14 +178,9 @@ export const dashboardController = {

enrollClient: async (req: Request, res: Response, next: NextFunction) => {
try {
const { email, password } = req.body;
const domain = req.session.client?.domain;
if (!domain) {
next(new Error('로그인 필요'));
return;
}
const { email, password, domain } = req.body;
const apiKey = await dashboardClientService.enrollClient(email, password, domain);
res.status(201).json({ message: '회원가입성공', apiKey });
res.status(201).json({ message: '회원가입 성공', apiKey });
} catch (err) {
next(err);
}
Expand Down
2 changes: 1 addition & 1 deletion src/middleware/errorHandle.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ export const errorHandle = (err: Error, req: Request, res: Response, next: NextF
return next(err);
}
if (
err.message === '로그인 에러' ||
err.message === '도메인 에러' ||
err.message === 'apiKey 인증실패' ||
err.message === '로그인 필요' ||
err.message === '이미 로그아웃'
Expand Down
10 changes: 10 additions & 0 deletions src/middleware/hasDashboardDomain.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import { NextFunction, Request, Response } from 'express';

export const hasDashboardDomain = (req: Request, res: Response, next: NextFunction) => {
const domain = req.session.client?.domain;
if (!domain) {
return next(new Error('도메인 에러'));
}
res.locals.dashboardDomain = domain;
next();
};
82 changes: 67 additions & 15 deletions src/routes/dashBoardRoutes.ts
Original file line number Diff line number Diff line change
@@ -1,26 +1,78 @@
import express from 'express';
import { dashboardController } from '../controllers/dashboardController';

import { hasDashboardDomain } from '../middleware/hasDashboardDomain';
export const dashboardRouter = express.Router();

dashboardRouter.get('/dashboard/onlineUsersCount', dashboardController.getOnlineUsersCount);
dashboardRouter.get('/dashboard/browsersStats', dashboardController.getBrowserStats);
dashboardRouter.get('/dashboard/osStats', dashboardController.getOsStats);
dashboardRouter.get('/dashboard/deviceStats', dashboardController.getDeviceStats);
dashboardRouter.get('/dashboard/resolutionStats', dashboardController.getResolutionStats);
dashboardRouter.get('/dashboard/languageStats', dashboardController.getLanguageStats);
dashboardRouter.get('/dashboard/countryStats', dashboardController.getCountryStats);
dashboardRouter.get('/dashboard/visitedRate', dashboardController.getVisitedUsersRate);
dashboardRouter.get('/dashboard/referrer', dashboardController.getReferrerStats);
dashboardRouter.get('/dashboard/loadTime', dashboardController.getAveragePageLoadTime);
dashboardRouter.get('/dashboard/visitorsPageByPeriodCount', dashboardController.getPageViewCount);
dashboardRouter.get(
'/dashboard/onlineUsersCount',
hasDashboardDomain,
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

근데 도메인 유무를 왜 확인해야 하나요? 무조건 있어야 하는 거 아닌가요? 스키마상으로 domain이 nullable한가요?
로그인 여부를 확인해야지 왜 도메인 유무를 확인하는지 알기가 어렵네요.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

스키마상으로 domain은 nullable 하지않습니다 무조건 존재합니다 저렇게 코드를 작성한 이유는 타입스크립트에서 인식할수있도록 작성했습니다.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  • '저렇게'가 뭔가요?
  • nullable 하지 않으면 타입스트립트도 그렇게 이해를 할 텐데 왜 따로 처리를 해야 하나요?
  • nullable 하지 않은 domain 유무를 왜 미들웨어로 판단해야 하냐는 질문이었어요.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  • '저렇게'란 domain이 nullable하지않은데 if문으로 있는지 없는지 판단하게 한 코드입니다
  • 현재 로그인했을때 session에 저장되는 사용자의 도메인으로 호출하는 방법을 사용하고있습니다 따라서 인식을 못하고있는것 같습니다
  • 맞습니다 nullable 하지않은 domain을 미들웨어로 판단해야하는것이 지금 타입스크립트가 인지를 못하고있어서였습니다
  • 지금 확인해보니 controller단에서 req.session.client?.domain을 가져와서 service단으로 넘겨주면 타입오류가 안나는것같습니다

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

이 코드리뷰에서 내부 구현은 관심사가 아니고요, hasDashboardDomain, 그러니까 domain 유무를 왜 확인하냐는 겁니다. 도메인은 무조건 있는 건데.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

그럼 위 댓글에서 말씀하신 '실제 세션 데이터'라는 건 어차피 실체가 없는 거 아닌가요?

Copy link
Copy Markdown
Collaborator Author

@nemo0824 nemo0824 Mar 12, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

express-session의 타입 정의를 보게되면 session: session.Session & Partial<session.SessionData>;
SessionData가 Partial<>감싸져있는데 SessionData내부에서 client를 넣어도 역시 Partial 때문에 보장이 안되고 있습니다

멘토님이 말씀하신 Request를 확장은 혹시 clinet를 넣어 확장하여 미들웨어에서 req.client = req.session.client 이렇게 우회하라는 말씀이신가요?

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

interface AuthenticatedRequest extends Request {
  session: Session & {
    client: {
      email: string;
      domain: string;
    }
  }
}

만들고

async (req: AuthenticatedRequest, res: Response) => {}

이렇게 써보세요.

Copy link
Copy Markdown
Collaborator Author

@nemo0824 nemo0824 Mar 13, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

제시해주신 방법도 전에 시도해봤었는데 라이터 쪽 RequestHandler 타입에러가 있어서 실패했었습니다
그러면 RequsetHandler 까지 타입 확장해서 해결해야하는 문제일가요?

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

HOF 사용하면 될 것 같은데 힘들면 라우터 쪽에 as RequestHandler 사용하세요~

dashboardController.getOnlineUsersCount
);
dashboardRouter.get(
'/dashboard/browsersStats',
hasDashboardDomain,
dashboardController.getBrowserStats
);
dashboardRouter.get('/dashboard/osStats', hasDashboardDomain, dashboardController.getOsStats);
dashboardRouter.get(
'/dashboard/deviceStats',
hasDashboardDomain,
dashboardController.getDeviceStats
);
dashboardRouter.get(
'/dashboard/resolutionStats',
hasDashboardDomain,
dashboardController.getResolutionStats
);
dashboardRouter.get(
'/dashboard/languageStats',
hasDashboardDomain,
dashboardController.getLanguageStats
);
dashboardRouter.get(
'/dashboard/countryStats',
hasDashboardDomain,
dashboardController.getCountryStats
);
dashboardRouter.get(
'/dashboard/visitedRate',
hasDashboardDomain,
dashboardController.getVisitedUsersRate
);
dashboardRouter.get(
'/dashboard/referrer',
hasDashboardDomain,
dashboardController.getReferrerStats
);
dashboardRouter.get(
'/dashboard/loadTime',
hasDashboardDomain,
dashboardController.getAveragePageLoadTime
);
dashboardRouter.get(
'/dashboard/visitorsPageByPeriodCount',
hasDashboardDomain,
dashboardController.getPageViewCount
);
dashboardRouter.get(
'/dashboard/perPageAverageScrollDepth',
dashboardController.getPerPageAverageScrollDepth
);
dashboardRouter.get('/dashboard/bounceRate', dashboardController.getPerPageBounceRate);
dashboardRouter.get(`/dashboard/visitorsByPeriodCount`, dashboardController.getVisitorsByPeriod);
dashboardRouter.get(`/dashboard/totalVisitorsCount`, dashboardController.getTotalVisitors);
dashboardRouter.get(
'/dashboard/bounceRate',
hasDashboardDomain,
dashboardController.getPerPageBounceRate
);
dashboardRouter.get(
'/dashboard/visitorsByPeriodCount',
hasDashboardDomain,
dashboardController.getVisitorsByPeriod
);
dashboardRouter.get(
'/dashboard/totalVisitorsCount',
hasDashboardDomain,
dashboardController.getTotalVisitors
);
dashboardRouter.post('/dashboard/enrollClient', dashboardController.enrollClient);
dashboardRouter.post('/dashboard/loginClient', dashboardController.loginClient);
dashboardRouter.post('/dashboard/logoutClient', dashboardController.logoutClient);
Loading