728x90
테스트 링크 : localhost:3002
토큰을 사용하는게 어려웠습니다. 이젠 토큰 보내고 쓸줄 알아요
// DOM 로드 후 이벤트 리스너 연결
document.addEventListener('DOMContentLoaded', () => {
const button = document.getElementById('viewCashBtn');
button.addEventListener('click', fetchCash);
});
라우터 파일 기능 사용하기 (토큰 사용하기)
// 버튼 클릭 시 캐시 조회 요청
async function fetchCash() {
const accessToken = localStorage.getItem('accessToken'); // 로그인 후 저장된 토큰을 가져옴
const email = localStorage.getItem('email'); // // 헤더에 이메일 추가
if (!accessToken) {
document.getElementById('resultMessage').innerText =
'로그인이 필요합니다.';
return;
}
try {
const response = await fetch('/api/cash', {
method: 'GET',
headers: {
Authorization: `Bearer ${accessToken}`, // 저장된 토큰을 Authorization 헤더에 추가
'x-info': email, // 헤더에 이메일 추가
'Content-Type': 'application/json',
},
});
body에서 전달할 내용이 있는 경우
const response = await fetch('/api/cash/gift', {
method: 'POST',
headers: {
Authorization: `Bearer ${accessToken}`, // 저장된 토큰을 Authorization 헤더에 추가
'x-info': email, // 헤더에 이메일 추가
'Content-Type': 'application/json',
},
body: JSON.stringify({ receiverEmail, amount, password }),
});
라우터에서 토큰인증, body사용하기
/**O 캐시 구매API email, 캐시, 비번! **/
router.post('/cash/payment', authM, async (req, res, next) => {
const { accountId } = req.account;
const { buyCash, password } = req.body;
const bCash = +buyCash;
더보기
Project_FO/src/routes/cash/cash.router.js
// routes/cash.router.js
import express from 'express';
import bcrypt from 'bcrypt';
import bcrypt from 'bcrypt';
import { prisma } from '../../utils/prisma/index.js';
import authM from '../../middlewares/auth.js';
const router = express.Router();
// const bcryptPassword = await bcrypt.hash(password, 10); // 생성
// const isPasswordMatch = await bcrypt.compare(password, account.password); // 비교
/** Lucky캐시API email **/
router.get('/cash/lucky', authM, async (req, res, next) => {
const { accountId } = req.account;
try {
// email이 있는지 확인
const account = await prisma.account.findFirst({
where: { accountId },
select: {
email: true,
manager: {
select: {
// 수정: `select`로 변경
cash: true,
managerId: true,
},
},
},
});
// 있으면
if (!account) {
return res
.status(404)
.json({ message: '존재하지 않는 Email 입니다.' });
}
const giftCash = Math.floor(Math.random() * 200) + 20;
if (account.manager.cash + giftCash > 2147483640) {
await prisma.manager.update({
where: { managerId: account.manager.managerId },
data: { cash: 2147483640 },
});
return res.status(200).json({
message: `LUCKY!!! 캐시함이 가득 채워졌습니다.`,
cash: giftCash,
});
} else {
// originalCache는 객체 형태. originalCache -> originalCache.cash
await prisma.manager.update({
where: { managerId: account.manager.managerId },
data: { cash: account.manager.cash + giftCash },
});
return res.status(200).json({
message: `LUCKY!!! ${giftCash}캐시를 받았습니다.`,
cash: giftCash,
});
}
} catch (error) {
console.error('Error lucky cash:', error);
return res.status(500).json({ message: 'Internal server error' });
}
});
/**O 캐시 구매API email, 캐시, 비번! **/
router.post('/cash/payment', authM, async (req, res, next) => {
const { accountId } = req.account;
const { buyCash, password } = req.body;
const bCash = +buyCash;
try {
// 구매할 캐시가 유효한지 확인
console.log(bCash);
if (!bCash || bCash <= 0) {
return res.status(400).json({
message: '구매하려는 캐시는 0 이상의 정수를 입력해주세요.',
});
}
if (+bCash > 2100000000) {
return res.status(400).json({
message: '21억보다 작은 캐시를 입력해주세요.',
});
}
// 이메일과 비밀번호로 Account 조회
const account = await prisma.account.findUnique({
where: { accountId },
select: {
password: true,
manager: {
select: { managerId: true, cash: true },
},
},
});
// 없으면
if (!account) {
return res
.status(404)
.json({ message: '존재하지 않는 Email 입니다.' });
}
// 비번확인
const isPasswordMatch = await bcrypt.compare(
password,
account.password
); // (password, account.password);
if (!isPasswordMatch) {
return res
.status(404)
.json({ message: '비밀번호가 일치하지 않습니다.' });
}
if (account.manager.cash + bCash > 2147483640) {
return res.status(400).json({
message:
'매니저는 2,147,483,640보다 큰 캐시를 보유할 수 없습니다.',
});
}
// Manager 업데이트
await prisma.manager.update({
where: { managerId: account.manager.managerId },
data: { cash: account.manager.cash + bCash }, // 문자로 나왔음
});
return res
.status(200)
.json({ message: `${bCash}캐시를 결제하셧습니다.` });
} catch (error) {
console.error('Error fetching cash data:', error);
return res
.status(500)
.json({ message: '캐시 구매 Internal server error' });
}
});
/**O 캐시 조회API email, 비번 추가하기 **/
router.get('/cash', authM, async (req, res, next) => {
console.log('조회');
const { accountId } = req.account;
// 이메일 유효성 검사
// if (!email || typeof email !== 'string') {
// return res.status(400).json({ message: 'Invalid email parameter' });
// }
try {
const account = await prisma.account.findFirst({
where: { accountId },
select: {
email: true,
password: true,
password: true,
manager: {
select: {
cash: true,
managerId: true,
},
},
},
});
if (!account) {
return res.status(404).json({ message: 'User not found' });
}
return res.status(200).json({
data: { email: account.email, cash: account.manager.cash },
});
} catch (error) {
console.error('Error fetching cash data:', error);
return res
.status(500)
.json({ message: '캐시조회 Internal server error' });
}
});
/** 1. 다른 유저에게 캐시 선물API 비번!**/
router.post('/cash/gift', authM, async (req, res, next) => {
try {
const accountId = req.account.accountId;
const { receiverEmail, amount, password } = req.body;
const parsedAmount = +amount;
// 입력정보 확인
if (!receiverEmail || !parsedAmount || !password) {
return res.status(400).json({
error: '수신자 이메일, 금액, 비밀번호를 모두 입력해주세요.',
});
}
if (parsedAmount > 2100000000) {
return res.status(400).json({
message: '캐시는 21억보다 작은 정수를 입력해주세요.',
});
}
// 송신자 정보 확인
const sender = await prisma.account.findUnique({
where: { accountId: Number(accountId) },
include: {
manager: true,
},
});
if (!sender) {
return res.status(404).json({
error: '송신자 계정을 찾을 수 없습니다.',
});
}
if (!sender.manager) {
return res.status(404).json({
error: '송신자의 매니저 정보를 찾을 수 없습니다.',
});
}
// 비밀번호 확인
const isPasswordMatch = await bcrypt.compare(password, sender.password);
if (!isPasswordMatch) {
return res.status(401).json({
error: '비밀번호가 일치하지 않습니다.',
});
}
// 수신자 확인
const receiver = await prisma.account.findUnique({
where: { email: receiverEmail },
include: {
manager: true,
},
});
if (!receiver) {
return res.status(404).json({
error: '수신자 계정을 찾을 수 없습니다.',
});
}
if (!receiver.manager) {
return res.status(404).json({
error: '수신자의 매니저 정보를 찾을 수 없습니다.',
});
}
if (parsedAmount < 1) {
return res.status(400).json({
// 404 -> 400으로 변경
error: '선물하는 금액을 1캐시 이상 입력해주세요.',
});
}
// 잔액 확인
if (sender.manager.cash < parsedAmount) {
return res.status(400).json({
error: '잔액이 부족합니다.',
});
}
if (receiver.manager.cash + parsedAmount > 2147483640) {
// 여기서 오류
return res.status(400).json({
message: '선물 캐시가 상대방의 캐시함의 빈자리보다 큽니다.',
});
}
// 트랜잭션으로 캐시 이동 처리
await prisma.$transaction([
prisma.manager.update({
where: { managerId: sender.manager.managerId },
data: { cash: sender.manager.cash - parsedAmount },
}),
prisma.manager.update({
where: { managerId: receiver.manager.managerId },
data: { cash: receiver.manager.cash + parsedAmount },
}),
]);
return res.status(200).json({
message: `${receiverEmail}님에게 ${parsedAmount}캐시를 선물했습니다.`,
});
} catch (error) {
console.error('Error gifting cash:', error);
return res
.status(500)
.json({ error: '캐시 선물 중 오류가 발생했습니다.' });
}
});
/** 2. 돈 불리기 ( 행운의 룰렛)API 비번!**/
router.post('/cash/roulette', authM, async (req, res, next) => {
console.log('여기');
const { accountId } = req.account;
const { betAmount, password } = req.body;
const betingAmount = +betAmount;
try {
// 입력정보 유효성 확인
const account = await prisma.account.findFirst({
where: { accountId },
select: {
email: true,
password: true,
manager: { select: { cash: true, managerId: true } },
},
});
// 이메일
if (!account) {
return res
.status(404)
.json({ message: '일치하는 이메일이 없습니다.' });
}
// 비번
const isPasswordMatch = await bcrypt.compare(
password,
account.password
);
if (!isPasswordMatch) {
return res
.status(404)
.json({ message: '비밀번호가 일치하지 않습니다.' });
}
// 캐시 보유금액 확인
if (!Number.isInteger(betingAmount) || betingAmount < 1) {
return res.status(404).json({
message: '캐시는 1 이상의 정수로 적어주세요.',
});
}
if (betingAmount > account.manager.cash) {
return res.status(404).json({
message: '보유 캐시보다 적은 금액만 걸 수 있습니다.',
});
}
if (betingAmount > 2100000000) {
return res.status(400).json({
message: '캐시는 21억보다 작은 정수를 입력해주세요.',
});
}
// 유저가 캐시를 걸면 n배로 돌려받기. 확률 설정하기
// 0.5배: 20% |1배: 50% |2배: 20% |5배: 8% |10배: 1.8% |50배: 0.2%
const roulette = Math.random() * 100; // 0 ~ 99.9999
let multiplyC = 0.0;
if (roulette <= 20) {
multiplyC = 0.5;
} else if (roulette <= 70) {
multiplyC = 1;
} else if (roulette <= 90) {
multiplyC = 2;
} else if (roulette <= 98) {
multiplyC = 5;
} else if (roulette <= 99.8) {
multiplyC = 10;
} else {
multiplyC = 50;
}
let batR = Math.floor(betingAmount * multiplyC);
if (account.manager.cash - betingAmount + batR > 2147483640) {
await prisma.manager.update({
where: { managerId: account.manager.managerId },
data: { cash: 2147483640 },
});
return res.status(200).json({
message: `${multiplyC}배에 당첨되셨습니다! 캐시함이 꽉 채워졌습니다.`,
});
} else {
await prisma.manager.update({
where: { managerId: account.manager.managerId },
data: { cash: account.manager.cash - betingAmount + batR },
});
return res.status(200).json({
message: `${multiplyC}배에 당첨되셨습니다! ${batR} 캐시를 획득하셨습니다.`,
});
}
} catch (error) {
console.error('Error fetching cash data:', error);
return res
.status(500)
.json({ message: '캐시 룰렛 Internal server error' });
}
});
/**O 3. 게임 승패로 캐시 증감API **/
// 게임 결과로 캐시 주고 뺐기
router.post('/cash/game-result', async (req, res, next) => {
const { winnerEmail, loserEmail, result, amount } = req.body;
console.log('루렛들어옴');
console.log('루렛들어옴');
try {
if (!winnerEmail || !loserEmail || !amount || amount <= 0) {
return res.status(400).json({
message:
'승자, 패자 이메일, 경기결과, 0 이상의 보상캐시을 입력해주세요.',
});
}
if (amount > 2100000000) {
return res.status(400).json({
message: '캐시는 21억보다 작은 정수를 입력해주세요.',
});
}
if (amount > 2100000000) {
return res.status(400).json({
message: '캐시는 21억보다 작은 정수를 입력해주세요.',
});
}
if (!result === 0 && !result === 1) {
return res.status(400).json({
message:
'승패는 무승부면 0을, 아니면 1을 정수 형태로 넣어주세요.',
});
}
// 승자 데이터 확인
const winner = await prisma.account.findFirst({
where: { email: winnerEmail },
select: { manager: { select: { cash: true, managerId: true } } },
});
if (!winner) {
return res
.status(404)
.json({ message: '승자 이메일이 존재하지 않습니다.' });
}
// 패자 데이터 확인
const loser = await prisma.account.findFirst({
where: { email: loserEmail },
select: { manager: { select: { cash: true, managerId: true } } },
});
if (!loser) {
return res
.status(404)
.json({ message: '패자 이메일이 존재하지 않습니다.' });
}
// result 승패 있으면 1, 무승부면 0 을 넣기
let winnerReward = 0;
let loserPenalty = 0;
if (result) {
winnerReward = amount;
loserPenalty = Math.max(-loser.manager.cash, -amount); // 최대 패널티는 가진 돈까지만
} else {
// 무승부: 배팅 금액의 절반씩 지급
winnerReward = Math.floor(amount / 2);
loserPenalty = Math.floor(amount / 2);
}
if (isNaN(winnerReward) || isNaN(loserPenalty)) {
throw new Error('캐시 계산1 중 오류가 발생했습니다.');
}
const winnerCashUpdate = winner.manager.cash + winnerReward;
const loserCashUpdate = loser.manager.cash + loserPenalty;
if (isNaN(winnerCashUpdate) || isNaN(loserCashUpdate)) {
throw new Error('캐시 계산2 중 오류가 발생했습니다.');
}
// 데이터 업데이트
await prisma.manager.update({
where: { managerId: winner.manager.managerId },
data: { cash: winnerCashUpdate },
});
await prisma.manager.update({
where: { managerId: loser.manager.managerId },
data: { cash: loserCashUpdate },
});
if (result) {
return res.status(200).json({
message: '경기보상을 받으세요!',
gameResult: `${winnerEmail} 승리`,
details: [
{ email: winnerEmail, change: `+${winnerReward} 캐시` },
{ email: loserEmail, change: `${loserPenalty} 캐시` },
],
});
} else {
return res.status(200).json({
message: '경기보상을 받으세요!',
gameResult: '무승부',
details: [
{ email: winnerEmail, change: `+${winnerReward} 캐시` },
{ email: loserEmail, change: `+${loserPenalty} 캐시` },
],
});
}
} catch (error) {
console.error('Error processing game result:', error);
return res
.status(500)
.json({ message: '캐시 승패 Internal server error' });
}
});
export default router;
Project_FO/public/cash/cash.html
<!-- V:\Sparta\Team\ch3_Futsal\cash\Project_FO\public\cash\cash.html -->
<!-- http://127.0.0.1:3002/cash/cash.html -->
<!DOCTYPE html>
<html lang="ko">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>캐시 상점</title>
<link rel="stylesheet" href="styles.css">
<script src="cashScript.js"></script>
</head>
<body>
<div class="container">
<header>
<h1>캐시 상점</h1>
</header>
<main>
<section id="cash-actions">
<section id="output">
<h5>캐시상점에 오신걸 환영합니다!</h5>
<div id="resultMessage"></div>
</section>
<div class="form-group">
<button id="viewCashBtn">My 캐시 조회</button>
</div>
<div class="form-group">
<button id="luckyCashBtn">Lucky 캐시</button>
</div>
<div class="form-group">
<!--캐시구매-->
<h2></h2>
<h1>캐시구매</h1>
<label for="buyCashInput">캐시 가격:</label>
<input type="number" id="buyCashInput" required />
<label for="passwordInput">비밀번호:</label>
<input type="password" id="passwordBInput" required />
<button id="buyCashBtn">캐시 구매</button>
</div>
<div class="form-group">
<!--캐시선물-->
<h2></h2>
<h1>캐시선물</h1>
<!-- <label for="senderEmail">My 이메일:</label>
<input type="senderEmail" id="senderEmail" required /> -->
<label for="receiverEmail">받는 사람 이메일:</label>
<input type="receiverEmail" id="receiverEmailInput" required />
<label for="amount">선물 캐시:</label>
<input type="amount" id="amountGInput" required />
<label for="password">비밀번호:</label>
<input type="password" id="passwordGInput" required />
<button id="giftCashBtn">캐시 선물하기</button>
</div>
<div class="form-group">
<!--룰렛-->
<h2></h2>
<h1>룰렛 캐시</h1>
<label for="amount">캐시 배팅:</label>
<input type="amount" id="amountRInput" required />
<label for="password">비밀번호:</label>
<input type="password" id="passwordRInput" required />
<button id="playRouletteBtn">룰렛 돌리기</button>
</div>
</section>
<section id="action-forms" style="display: none;">
<!-- Form templates will be dynamically inserted here -->
</section>
</main>
</div>
</body>
</html>
Project_FO/public/cash/cashScript.js
// DOM 로드 후 이벤트 리스너 연결
document.addEventListener('DOMContentLoaded', () => {
const button = document.getElementById('viewCashBtn');
button.addEventListener('click', fetchCash);
});
document.addEventListener('DOMContentLoaded', () => {
const button = document.getElementById('luckyCashBtn');
button.addEventListener('click', fetchLuck);
});
document.addEventListener('DOMContentLoaded', () => {
const button = document.getElementById('buyCashBtn');
button.addEventListener('click', fetchBuy);
});
document.addEventListener('DOMContentLoaded', () => {
const button = document.getElementById('giftCashBtn');
button.addEventListener('click', fetchGift);
});
document.addEventListener('DOMContentLoaded', () => {
const button = document.getElementById('playRouletteBtn');
button.addEventListener('click', fetchRoulett);
});
// 버튼 클릭 시 캐시 조회 요청
async function fetchCash() {
const accessToken = localStorage.getItem('accessToken'); // 로그인 후 저장된 토큰을 가져옴
const email = localStorage.getItem('email'); // // 헤더에 이메일 추가
if (!accessToken) {
document.getElementById('resultMessage').innerText =
'로그인이 필요합니다.';
return;
}
try {
const response = await fetch('/api/cash', {
method: 'GET',
headers: {
Authorization: `Bearer ${accessToken}`, // 저장된 토큰을 Authorization 헤더에 추가
'x-info': email, // 헤더에 이메일 추가
'Content-Type': 'application/json',
},
});
if (!response.ok) {
// 에러 메시지 출력
const errorData = await response.json();
document.getElementById('resultMessage').innerText =
`Error: ${errorData.message}`;
return;
}
const data = await response.json();
document.getElementById('resultMessage').innerText =
`이메일: ${data.data.email}, 캐시: ${data.data.cash}`;
} catch (error) {
console.error('Fetch error:', error);
document.getElementById('resultMessage').innerText =
'서버와 연결할 수 없습니다.';
}
}
async function fetchLuck() {
const accessToken = localStorage.getItem('accessToken'); // 로그인 후 저장된 토큰을 가져옴
const email = localStorage.getItem('email'); // // 헤더에 이메일 추가
if (!accessToken) {
document.getElementById('resultMessage').innerText =
'로그인이 필요합니다.';
return;
}
try {
const response = await fetch('/api/cash/lucky', {
method: 'GET',
headers: {
Authorization: `Bearer ${accessToken}`, // 저장된 토큰을 Authorization 헤더에 추가
'x-info': email, // 헤더에 이메일 추가
'Content-Type': 'application/json',
},
});
if (!response.ok) {
// 에러 메시지 출력
const errorData = await response.json();
document.getElementById('resultMessage').innerText =
`Error: ${errorData.message}`;
return;
}
const data = await response.json();
document.getElementById('resultMessage').innerText = `${data.message}`; // 수정
} catch (error) {
console.error('Fetch error:', error);
document.getElementById('resultMessage').innerText =
'서버와 연결할 수 없습니다.';
}
}
// + body
async function fetchBuy() {
const accessToken = localStorage.getItem('accessToken'); // 로그인 후 저장된 토큰을 가져옴
const email = localStorage.getItem('email'); // // 헤더에 이메일 추가
if (!accessToken) {
document.getElementById('resultMessage').innerText =
'로그인이 필요합니다.';
return;
}
const buyCash = document.getElementById('buyCashInput').value; // 입력된 buyCash 값
const password = document.getElementById('passwordBInput').value; // 입력된 password 값
try {
const response = await fetch('/api/cash/payment', {
method: 'POST',
headers: {
Authorization: `Bearer ${accessToken}`, // 저장된 토큰을 Authorization 헤더에 추가
'x-info': email, // 헤더에 이메일 추가'Content-Type': 'application/json',
'Content-Type': 'application/json',
},
body: JSON.stringify({ buyCash, password }),
});
if (!response.ok) {
// 에러 메시지 출력
const errorData = await response.json();
document.getElementById('resultMessage').innerText =
`Error: ${errorData.message}`;
return;
}
const data = await response.json();
document.getElementById('resultMessage').innerText = `${data.message}`;
} catch (error) {
console.error('Fetch error:', error);
document.getElementById('resultMessage').innerText =
'서버와 연결할 수 없습니다.';
}
}
// + body
async function fetchGift() {
const accessToken = localStorage.getItem('accessToken'); // 로그인 후 저장된 토큰을 가져옴
const email = localStorage.getItem('email'); // // 헤더에 이메일 추가
if (!accessToken) {
document.getElementById('resultMessage').innerText =
'로그인이 필요합니다.';
return;
}
const receiverEmail = document.getElementById('receiverEmailInput').value; // 입력된 buyCash 값
const amount = document.getElementById('amountGInput').value; // 입력된 buyCash 값
const password = document.getElementById('passwordGInput').value; // 입력된 password 값
try {
const response = await fetch('/api/cash/gift', {
method: 'POST',
headers: {
Authorization: `Bearer ${accessToken}`, // 저장된 토큰을 Authorization 헤더에 추가
'x-info': email, // 헤더에 이메일 추가
'Content-Type': 'application/json',
},
body: JSON.stringify({ receiverEmail, amount, password }),
});
if (!response.ok) {
// 에러 메시지 출력
const errorData = await response.json();
document.getElementById('resultMessage').innerText =
`Error: ${errorData.message}`;
return;
}
const data = await response.json();
document.getElementById('resultMessage').innerText = `${data.message}`;
} catch (error) {
console.error('Fetch error:', error);
document.getElementById('resultMessage').innerText =
'서버와 연결할 수 없습니다.';
}
}
// + body
async function fetchRoulett() {
const accessToken = localStorage.getItem('accessToken'); // 로그인 후 저장된 토큰을 가져옴
const email = localStorage.getItem('email'); // // 헤더에 이메일 추가
if (!accessToken) {
document.getElementById('resultMessage').innerText =
'로그인이 필요합니다.';
return;
}
const betAmount = document.getElementById('amountRInput').value; // 입력된 buyCash 값
const password = document.getElementById('passwordRInput').value; // 입력된 password 값
try {
const response = await fetch('/api/cash/roulette', {
method: 'POST',
headers: {
Authorization: `Bearer ${accessToken}`, // 저장된 토큰을 Authorization 헤더에 추가
'x-info': email, // 헤더에 이메일 추가
'Content-Type': 'application/json',
},
body: JSON.stringify({ betAmount, password }),
});
if (!response.ok) {
// 에러 메시지 출력
const errorData = await response.json();
document.getElementById('resultMessage').innerText =
`Error: ${errorData.message}`;
return;
}
const data = await response.json();
document.getElementById('resultMessage').innerText = `${data.message}`;
} catch (error) {
console.error('Fetch error:', error);
document.getElementById('resultMessage').innerText =
'서버와 연결할 수 없습니다.';
}
}
Project_FO/public/cash/ styles.css
/* 구글 폰트 import url */
@import url('https://fonts.googleapis.com/css2?family=Black+Han+Sans&family=Do+Hyeon&family=Gowun+Dodum&family=Orbit&family=Song+Myung&display=swap');
.header{
height: 200px;
}
body {
font-family: 'Orbit', sans-serif;
background-color: #f4f4f4;
display: flex; /* Flexbox 레이아웃 */
flex-direction: column;
/* height: 100vh; */
margin: 200px;
}
.container {
font-family: 'Orbit', sans-serif;
background: rgb(255, 255, 255);
padding: 50px; /* 박스 내부 여백 */
border-radius: 8px; /* 둥근 모서리 */
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1); /* 그림자 효과 */
display: flex;
flex-direction: column; /* 세로 정렬 */
justify-content: center; /* 수직 중앙 정렬 */
align-items: center; /* 수평 중앙 정렬 */
width: 100%;
height: 100%;
/* max-width: 400px; 박스 최대 너비 */
margin: auto; /* 화면 중앙 정렬 */
}
h1 {
text-align: center;
margin-bottom: 20px; /* 제목 아래 여백 */
}
#cash-actions{
position: relative; /* 요소를 이동시키기 위해 relative를 사용 */
padding: 10px;
}
#output{
border-radius: 8px; /* 둥근 모서리 */
background-color: #ecfce8;
padding: 5px 0;
text-align: center;
margin-bottom: 20px;
}
.form-group {
margin-bottom: 15px;
width: 100%; /* 입력 필드와 버튼 너비를 박스에 맞춤 */
}
label {
display: block;
margin-bottom: 5px;
font-weight: bold; /* 라벨 텍스트 강조 */
}
input {
width: 100%;
padding: 10px;
border: 1px solid #ddd;
border-radius: 5px;
box-sizing: border-box;
margin-top: 10px;
margin-bottom: 10px;
}
button {
font-family: 'Orbit', sans-serif;
width: 100%;
padding: 10px;
background-color: #28a745;
color: white;
border: none;
border-radius: 5px;
cursor: pointer;
font-size: 16px;
}
/* 버튼 안의 링크 스타일 제거 */
button a {
text-decoration: none; /* 밑줄 제거 */
color: inherit; /* 부모 요소의 글자색을 따름 */
}
/* 버튼을 클릭했을 때 글자색 유지 */
button a:visited {
color: inherit; /* 방문 후에도 글자색 유지 */
}
button:hover {
background-color: #218838;
}
#message {
margin-top: 20px;
text-align: center;
}
더보기
프론트엔드를 만들 때, refresh token으로 access token을 갱신하기 위해 헤더를 설정하고 데이터를 불러오던 과정이
세션 DB와 localStorage를 적절히 사용해서 구현을 해야 했는데
처음엔 방법을 몰라서 좀 헤맸습니다
알고 보니 페이지가 이동될 때마다 헤더가 날아가서 localStorage가 필요한 거더라고요
그래서 api를 요청할 때마다 email을 다시 헤더에 넣어주는 작업을 fetch를 통해
수행했더니 api와 잘 연동이 되었습니다
이제는 access token이 만료가 되어도 email을 통해 사용자의 refresh token을 db에서 읽어올 수 있습니다
refresh token이 유효하다면 access token이 갱신되는 방식으로요
'내일배움 과제 > CH3 풋살온라인게임' 카테고리의 다른 글
CH3 풋살 온라인 프로젝트 발표 (1) | 2024.12.09 |
---|---|
발표회 준비 (0) | 2024.12.06 |
튜터님 코드리뷰 (1) | 2024.12.06 |
1차 머지 후 오류 수정 (0) | 2024.12.05 |
캐시 기능 추가시 기능 추가 (0) | 2024.12.04 |