안녕하세요, 오늘은 제가 최근에 진행한 이력서 포트폴리오 프로젝트의 일환으로 개발한 안전한 Admin 페이지 구축에 관한 경험을 공유하려 합니다.

1. 프로젝트 배경 및 개요
이 프로젝트는 제 개인 이력서 및 포트폴리오 웹사이트를 개발하는 과정에서 시작되었습니다. 웹사이트의 콘텐츠를 효율적으로 관리하고 업데이트하기 위해 안전하고 사용하기 쉬운 admin 시스템이 필요했습니다. (About Me 컴포넌트가 정적으로 구성되어 있는데 이를 동적으로 쉽게 변경하게 설정하기 위함) 이를 위해 Flask를 이용한 백엔드 서버와 Next.js를 이용한 프론트엔드 애플리케이션을 구현하기로 결정했습니다.
주요 목표는 다음과 같았습니다:
- 안전한 관리자 인증 시스템 구축
- 이력서 및 포트폴리오 콘텐츠의 동적 관리
- 사용자 친화적인 admin 인터페이스 제공
이를 위해 JWT(JSON Web Token)를 이용한 사용자 세션 관리와 MongoDB를 활용한 데이터 저장소를 구현했습니다.
※ 백엔드에서 인증 서버 구현을 경험해보기 위해 해당 프로젝트를 진행한 것..
2. 기술 스택
백엔드
- Flask: 파이썬 기반의 마이크로 웹 프레임워크
- MongoDB: NoSQL 데이터베이스
- Authlib: JWT 생성 및 검증을 위한 라이브러리
- python-dotenv: 환경 변수 관리를 위한 라이브러리
프론트엔드
- Next.js: React 기반의 서버 사이드 렌더링 프레임워크
- Axios: HTTP 클라이언트 라이브러리
3. 백엔드 주요 기능 구현
a. 데이터베이스 연결
MongoDB와의 연결을 위해 PyMongo 라이브러리를 사용했습니다. 연결 정보는 보안을 위해 환경 변수로 관리했습니다.
def connect_mongo():
client = MongoClient(f"mongodb://{MONGO_USERNAME}:{MONGO_PASSWORD}@{MONGO_HOST}:{MONGO_PORT}/{MONGO_DB}")
db = client[MONGO_DB]
collection = db[MONGO_COLLECTION]
return collection
b. 사용자 인증
관리자 로그인 시 데이터베이스에서 사용자 정보를 확인하고, 일치하는 경우 JWT 토큰을 생성하여 쿠키로 전달합니다.
@admin_auth_bp.route('/login', methods=['POST'])
def login():
db = connect_mongo()
id = request.json.get('id')
pw = request.json.get('pw')
user = mongo_find_user(db, id, pw)
if user is not None:
token = generate_token(id)
token_str = token.decode('utf-8')
response = make_response(jsonify({"status": "success"}))
response.set_cookie('token', token_str, httponly=True, secure=True, samesite='Lax')
return response
else:
return jsonify({"status": "fail"}), 401
c. 토큰 생성 및 검증
JWT를 이용해 토큰을 생성하고 검증하는 함수를 구현했습니다. 토큰의 유효 기간은 1시간으로 설정했습니다.
def generate_token(username):
header = {"alg": "HS256"}
payload = {
"username": username,
"exp": datetime.now(timezone.utc) + timedelta(hours=1)
}
token = jwt.encode(header, payload, JWT_SECRET_KEY)
return token
def verify_token(token):
try:
decoded = jwt.decode(token, JWT_SECRET_KEY)
if datetime.now(timezone.utc) > datetime.fromtimestamp(decoded['exp'], timezone):
return None
return decoded
except Exception as e:
return None
d. 보호된 라우트
토큰 검증을 통해 인증된 관리자만 접근할 수 있는 보호된 라우트를 구현했습니다.
@admin_auth_bp.route('/protected', methods=['GET'])
def protected():
token = request.cookies.get('token')
if not token:
return jsonify({"status": "fail", "message": "Token missing"}), 401
decoded = verify_token(token)
if not decoded:
return jsonify({"status": "fail", "message": "Invalid or expired token"}), 401
return jsonify({"status": "success", "message": "Access granted to protected route"})
4. 프론트엔드 주요 기능 구현
a. 관리자 로그인 페이지 (admin.js)
Next.js를 사용하여 관리자 로그인 페이지를 구현했습니다. 이 페이지는 이력서 포트폴리오 웹사이트의 관리자만 접근할 수 있도록 설계되었습니다.
import React, { useState } from 'react';
import { useRouter } from 'next/router';
import styles from '../styles/Admin.module.css';
import axios from 'axios';
function Admin() {
const [id, setId] = useState('');
const [password, setPassword] = useState('');
const router = useRouter();
const handleSubmit = async (e) => {
e.preventDefault();
const requestData = {
"id": id,
"pw": password
};
try {
const response = await axios.post('https://api.jongwook.xyz/auth/login', requestData, { withCredentials: true });
if (response.data.status === 'success') {
alert("Login successful!");
window.location.href = 'https://resume.jongwook.xyz/management';
} else {
alert("Login failed!");
}
} catch(error) {
console.error('Error during login:', error);
alert("Login failed!");
}
};
// JSX for the login form
return (
<div className={styles.container}>
<div className={styles.loginBox}>
<h2 className={styles.title}>Portfolio Admin</h2>
<form className={styles.form} onSubmit={handleSubmit}>
{/* Form inputs */}
</form>
</div>
</div>
);
}
export default Admin;
b. 포트폴리오 관리 페이지 (management.js)
이 페이지는 인증된 관리자만 접근할 수 있도록 구현했습니다. 여기서 관리자는 이력서와 포트폴리오 콘텐츠를 업데이트할 수 있습니다.
import React, { useEffect, useState } from 'react';
import { useRouter } from 'next/router';
import axios from 'axios';
function Management() {
const [authorized, setAuthorized] = useState(false);
const router = useRouter();
useEffect(() => {
const checkAuthorization = async () => {
try {
const response = await axios.get('https://api.jongwook.xyz/auth/protected', { withCredentials: true });
if (response.data.status === 'success') {
setAuthorized(true);
} else {
router.push('/admin');
}
} catch (error) {
console.error('Error checking authorization:', error);
router.push('/admin');
}
};
checkAuthorization();
}, []);
if (!authorized) {
return <div>Loading...</div>;
}
return (
<div>
<h1>Portfolio Management</h1>
<p>Here you can update your resume and portfolio content.</p>
{/* Add forms or components for updating portfolio content */}
</div>
);
}
export default Management;
5. 보안 고려사항
이력서와 포트폴리오 데이터의 무단 수정을 방지하기 위해 다음과 같은 보안 조치를 구현했습니다:
- HTTPS 사용: 모든 통신은 HTTPS를 통해 이루어지도록 설정했습니다.
- HttpOnly 쿠키: JWT 토큰을 HttpOnly 쿠키로 저장하여 XSS 공격으로부터 보호했습니다.
- 환경 변수 사용: 민감한 정보는 모두 환경 변수로 관리하여 코드 노출 시에도 보안을 유지할 수 있도록 했습니다.
- 토큰 만료: JWT 토큰에 만료 시간을 설정하여 토큰 탈취 시의 위험을 최소화했습니다.
- CORS 설정: 백엔드에서 적절한 CORS 설정을 통해 허용된 도메인에서만 API 접근이 가능하도록 했습니다.
6. 결론 및 향후 개선 사항
이번 프로젝트를 통해 이력서 포트폴리오 웹사이트를 위한 안전하고 효율적인 admin 시스템을 구축할 수 있었습니다. Flask와 Next.js의 조합은 백엔드와 프론트엔드 모두에서 빠른 개발과 유연한 확장성을 제공했습니다.
향후 개선 사항으로는 다음과 같은 것들을 고려하고 있습니다:
- 보안 강화:
- 다중 요소 인증(MFA) 구현: 관리자 계정의 보안을 강화하기 위해 SMS나 인증 앱을 통한 2단계 인증 도입
- 로그인 시도 제한: 무차별 대입 공격을 방지하기 위해 일정 횟수 이상의 로그인 실패 시 계정 잠금 기능 구현
- 사용자 경험 개선:
- 반응형 디자인: 다양한 디바이스에서 원활하게 관리 페이지를 사용할 수 있도록 반응형 디자인 적용
- 성능 최적화:
- 캐싱 전략 구현: 자주 접근하는 데이터에 대한 캐싱을 통해 응답 시간 개선
- 모니터링 및 로깅:
- 상세한 로깅 시스템: 보안 이벤트 및 사용자 활동에 대한 상세 로그 기록
- 실시간 모니터링: 시스템 성능 및 보안 이슈를 실시간으로 모니터링할 수 있는 대시보드 구현
이상으로 이력서 포트폴리오 프로젝트를 위한 Flask와 Next.js 기반 Admin 시스템 구현 경험에 대한 공유를 마치겠습니다.
※ 참고사항 : 현재 관리자 페이지의 경우 외부로 노출되어 있는 취약점이 있는 상태임 => 일반적으론 ACL 처리 등을 통해 정해진 사용자만 접근할 수 있도록 처리해야 함으로 개발 시 참고해주세요.
'Project > resume' 카테고리의 다른 글
Zustand와 Flask 미들웨어로 구현한 인증 시스템 : Refresh Token과 CORS 에러 해결 (0) | 2024.09.06 |
---|---|
AI Chatbot 응답 개선 : 프론트엔드 코드 최적화 (0) | 2024.08.24 |
MongoDB, Flask, Next.js 를 활용한 동적 블로그 컴포넌트 구현 (0) | 2024.08.17 |
AI Chatbot 추천 질문 시스템 구현 / OpenAI Assistant Intruction 을 활용한 AI 응답 지정 (0) | 2024.08.17 |
[Github Actions] CI/CD 파이프라인 구축 / Github Actions 사용법 / Snyk Application CI 파이프라인 통합 / EC2 배포 자동화 (0) | 2024.08.13 |
안녕하세요, 오늘은 제가 최근에 진행한 이력서 포트폴리오 프로젝트의 일환으로 개발한 안전한 Admin 페이지 구축에 관한 경험을 공유하려 합니다.

1. 프로젝트 배경 및 개요
이 프로젝트는 제 개인 이력서 및 포트폴리오 웹사이트를 개발하는 과정에서 시작되었습니다. 웹사이트의 콘텐츠를 효율적으로 관리하고 업데이트하기 위해 안전하고 사용하기 쉬운 admin 시스템이 필요했습니다. (About Me 컴포넌트가 정적으로 구성되어 있는데 이를 동적으로 쉽게 변경하게 설정하기 위함) 이를 위해 Flask를 이용한 백엔드 서버와 Next.js를 이용한 프론트엔드 애플리케이션을 구현하기로 결정했습니다.
주요 목표는 다음과 같았습니다:
- 안전한 관리자 인증 시스템 구축
- 이력서 및 포트폴리오 콘텐츠의 동적 관리
- 사용자 친화적인 admin 인터페이스 제공
이를 위해 JWT(JSON Web Token)를 이용한 사용자 세션 관리와 MongoDB를 활용한 데이터 저장소를 구현했습니다.
※ 백엔드에서 인증 서버 구현을 경험해보기 위해 해당 프로젝트를 진행한 것..
2. 기술 스택
백엔드
- Flask: 파이썬 기반의 마이크로 웹 프레임워크
- MongoDB: NoSQL 데이터베이스
- Authlib: JWT 생성 및 검증을 위한 라이브러리
- python-dotenv: 환경 변수 관리를 위한 라이브러리
프론트엔드
- Next.js: React 기반의 서버 사이드 렌더링 프레임워크
- Axios: HTTP 클라이언트 라이브러리
3. 백엔드 주요 기능 구현
a. 데이터베이스 연결
MongoDB와의 연결을 위해 PyMongo 라이브러리를 사용했습니다. 연결 정보는 보안을 위해 환경 변수로 관리했습니다.
def connect_mongo():
client = MongoClient(f"mongodb://{MONGO_USERNAME}:{MONGO_PASSWORD}@{MONGO_HOST}:{MONGO_PORT}/{MONGO_DB}")
db = client[MONGO_DB]
collection = db[MONGO_COLLECTION]
return collection
b. 사용자 인증
관리자 로그인 시 데이터베이스에서 사용자 정보를 확인하고, 일치하는 경우 JWT 토큰을 생성하여 쿠키로 전달합니다.
@admin_auth_bp.route('/login', methods=['POST'])
def login():
db = connect_mongo()
id = request.json.get('id')
pw = request.json.get('pw')
user = mongo_find_user(db, id, pw)
if user is not None:
token = generate_token(id)
token_str = token.decode('utf-8')
response = make_response(jsonify({"status": "success"}))
response.set_cookie('token', token_str, httponly=True, secure=True, samesite='Lax')
return response
else:
return jsonify({"status": "fail"}), 401
c. 토큰 생성 및 검증
JWT를 이용해 토큰을 생성하고 검증하는 함수를 구현했습니다. 토큰의 유효 기간은 1시간으로 설정했습니다.
def generate_token(username):
header = {"alg": "HS256"}
payload = {
"username": username,
"exp": datetime.now(timezone.utc) + timedelta(hours=1)
}
token = jwt.encode(header, payload, JWT_SECRET_KEY)
return token
def verify_token(token):
try:
decoded = jwt.decode(token, JWT_SECRET_KEY)
if datetime.now(timezone.utc) > datetime.fromtimestamp(decoded['exp'], timezone):
return None
return decoded
except Exception as e:
return None
d. 보호된 라우트
토큰 검증을 통해 인증된 관리자만 접근할 수 있는 보호된 라우트를 구현했습니다.
@admin_auth_bp.route('/protected', methods=['GET'])
def protected():
token = request.cookies.get('token')
if not token:
return jsonify({"status": "fail", "message": "Token missing"}), 401
decoded = verify_token(token)
if not decoded:
return jsonify({"status": "fail", "message": "Invalid or expired token"}), 401
return jsonify({"status": "success", "message": "Access granted to protected route"})
4. 프론트엔드 주요 기능 구현
a. 관리자 로그인 페이지 (admin.js)
Next.js를 사용하여 관리자 로그인 페이지를 구현했습니다. 이 페이지는 이력서 포트폴리오 웹사이트의 관리자만 접근할 수 있도록 설계되었습니다.
import React, { useState } from 'react';
import { useRouter } from 'next/router';
import styles from '../styles/Admin.module.css';
import axios from 'axios';
function Admin() {
const [id, setId] = useState('');
const [password, setPassword] = useState('');
const router = useRouter();
const handleSubmit = async (e) => {
e.preventDefault();
const requestData = {
"id": id,
"pw": password
};
try {
const response = await axios.post('https://api.jongwook.xyz/auth/login', requestData, { withCredentials: true });
if (response.data.status === 'success') {
alert("Login successful!");
window.location.href = 'https://resume.jongwook.xyz/management';
} else {
alert("Login failed!");
}
} catch(error) {
console.error('Error during login:', error);
alert("Login failed!");
}
};
// JSX for the login form
return (
<div className={styles.container}>
<div className={styles.loginBox}>
<h2 className={styles.title}>Portfolio Admin</h2>
<form className={styles.form} onSubmit={handleSubmit}>
{/* Form inputs */}
</form>
</div>
</div>
);
}
export default Admin;
b. 포트폴리오 관리 페이지 (management.js)
이 페이지는 인증된 관리자만 접근할 수 있도록 구현했습니다. 여기서 관리자는 이력서와 포트폴리오 콘텐츠를 업데이트할 수 있습니다.
import React, { useEffect, useState } from 'react';
import { useRouter } from 'next/router';
import axios from 'axios';
function Management() {
const [authorized, setAuthorized] = useState(false);
const router = useRouter();
useEffect(() => {
const checkAuthorization = async () => {
try {
const response = await axios.get('https://api.jongwook.xyz/auth/protected', { withCredentials: true });
if (response.data.status === 'success') {
setAuthorized(true);
} else {
router.push('/admin');
}
} catch (error) {
console.error('Error checking authorization:', error);
router.push('/admin');
}
};
checkAuthorization();
}, []);
if (!authorized) {
return <div>Loading...</div>;
}
return (
<div>
<h1>Portfolio Management</h1>
<p>Here you can update your resume and portfolio content.</p>
{/* Add forms or components for updating portfolio content */}
</div>
);
}
export default Management;
5. 보안 고려사항
이력서와 포트폴리오 데이터의 무단 수정을 방지하기 위해 다음과 같은 보안 조치를 구현했습니다:
- HTTPS 사용: 모든 통신은 HTTPS를 통해 이루어지도록 설정했습니다.
- HttpOnly 쿠키: JWT 토큰을 HttpOnly 쿠키로 저장하여 XSS 공격으로부터 보호했습니다.
- 환경 변수 사용: 민감한 정보는 모두 환경 변수로 관리하여 코드 노출 시에도 보안을 유지할 수 있도록 했습니다.
- 토큰 만료: JWT 토큰에 만료 시간을 설정하여 토큰 탈취 시의 위험을 최소화했습니다.
- CORS 설정: 백엔드에서 적절한 CORS 설정을 통해 허용된 도메인에서만 API 접근이 가능하도록 했습니다.
6. 결론 및 향후 개선 사항
이번 프로젝트를 통해 이력서 포트폴리오 웹사이트를 위한 안전하고 효율적인 admin 시스템을 구축할 수 있었습니다. Flask와 Next.js의 조합은 백엔드와 프론트엔드 모두에서 빠른 개발과 유연한 확장성을 제공했습니다.
향후 개선 사항으로는 다음과 같은 것들을 고려하고 있습니다:
- 보안 강화:
- 다중 요소 인증(MFA) 구현: 관리자 계정의 보안을 강화하기 위해 SMS나 인증 앱을 통한 2단계 인증 도입
- 로그인 시도 제한: 무차별 대입 공격을 방지하기 위해 일정 횟수 이상의 로그인 실패 시 계정 잠금 기능 구현
- 사용자 경험 개선:
- 반응형 디자인: 다양한 디바이스에서 원활하게 관리 페이지를 사용할 수 있도록 반응형 디자인 적용
- 성능 최적화:
- 캐싱 전략 구현: 자주 접근하는 데이터에 대한 캐싱을 통해 응답 시간 개선
- 모니터링 및 로깅:
- 상세한 로깅 시스템: 보안 이벤트 및 사용자 활동에 대한 상세 로그 기록
- 실시간 모니터링: 시스템 성능 및 보안 이슈를 실시간으로 모니터링할 수 있는 대시보드 구현
이상으로 이력서 포트폴리오 프로젝트를 위한 Flask와 Next.js 기반 Admin 시스템 구현 경험에 대한 공유를 마치겠습니다.
※ 참고사항 : 현재 관리자 페이지의 경우 외부로 노출되어 있는 취약점이 있는 상태임 => 일반적으론 ACL 처리 등을 통해 정해진 사용자만 접근할 수 있도록 처리해야 함으로 개발 시 참고해주세요.
'Project > resume' 카테고리의 다른 글
Zustand와 Flask 미들웨어로 구현한 인증 시스템 : Refresh Token과 CORS 에러 해결 (0) | 2024.09.06 |
---|---|
AI Chatbot 응답 개선 : 프론트엔드 코드 최적화 (0) | 2024.08.24 |
MongoDB, Flask, Next.js 를 활용한 동적 블로그 컴포넌트 구현 (0) | 2024.08.17 |
AI Chatbot 추천 질문 시스템 구현 / OpenAI Assistant Intruction 을 활용한 AI 응답 지정 (0) | 2024.08.17 |
[Github Actions] CI/CD 파이프라인 구축 / Github Actions 사용법 / Snyk Application CI 파이프라인 통합 / EC2 배포 자동화 (0) | 2024.08.13 |