diff --git a/auction-server/.env-example b/auction-server/.env-example
deleted file mode 100644
index e8460690..00000000
--- a/auction-server/.env-example
+++ /dev/null
@@ -1,10 +0,0 @@
-DB_TYPE=mysql
-DB_HOST=honeybee.palda.shop
-DB_DOCKER_COMPOSE_SERVICE_HOST=mysql-server
-DB_PORT=3306
-DB_USER=boost
-DB_PASSWORD=boost
-DB_NAME=palda
-
-CHAT_SERVER=http://chat.palda.shop:4000
-CHAT_DEV_SERVER=http://chat-server:4000
diff --git a/auction-server/.env.example b/auction-server/.env.example
index 518cae70..db9c8f36 100644
--- a/auction-server/.env.example
+++ b/auction-server/.env.example
@@ -1,3 +1,15 @@
+DB_TYPE=
+DB_HOST=
+DB_DOCKER_COMPOSE_SERVICE_HOST=
+DB_PORT=
+DB_USER=
+DB_PASSWORD=
+DB_NAME=
+
+CHAT_SERVER=
+CHAT_DEV_SERVER=
+
+
### Mail
MAIL_ID=
MAIL_PASSWORD=
diff --git a/auction-server/index.js b/auction-server/index.js
index 4d1cdaad..8001eefa 100644
--- a/auction-server/index.js
+++ b/auction-server/index.js
@@ -55,7 +55,7 @@ let queue = [];
//1분 반복 예시 "*/1 * * * *"
//[작업 할당 클론]
-cron.schedule("*/10 * * * * *", () => {
+cron.schedule("*/2 * * * * *", () => {
const now = moment().format("YYYY-MM-DD HH:mm:ss");
console.log(now);
pool.query(
@@ -87,7 +87,7 @@ cron.schedule("*/10 * * * * *", () => {
});
// [작업 처리 클론]
-cron.schedule("*/10 * * * * *", () => {
+cron.schedule("*/2 * * * * *", () => {
if (queue.length === 0) return console.log("QUEUE IS EMPTY");
console.dir(queue);
console.dir(queue.length);
diff --git a/auction-server/mailService.js b/auction-server/mailService.js
index c3448ca4..ecbe2b74 100644
--- a/auction-server/mailService.js
+++ b/auction-server/mailService.js
@@ -1,20 +1,26 @@
-const axios = require("axios")
+const axios = require("axios");
const mailTemplate = (isSeller, isSold, content) => `
세상의 모든 중고 경매 팔다 알림메일 입니다.
${isSeller ? "등록하신" : "입찰하신"} ${content}이
${
- isSeller ? (isSold ? "판매되었" : "유찰되었") : isSold ? "낙찰되었" : "유찰되었"
- }습니다.
+ isSeller
+ ? isSold
+ ? "판매되었"
+ : "유찰되었"
+ : isSold
+ ? "낙찰되었"
+ : "유찰되었"
+ }습니다.
팔다에 접속해 확인하시기 바랍니다.
-`
+`;
const mailTitleTemplate = (isSeller, isSold) => {
- if (isSeller) return `${isSold ? "판매" : "유찰"} 되었습니다.`
- return `${isSold ? "낙찰" : "유찰"} 되었습니다.`
-}
+ if (isSeller) return `${isSold ? "판매" : "유찰"} 되었습니다.`;
+ return `${isSold ? "낙찰" : "유찰"} 되었습니다.`;
+};
const mailFooter = `
중고 물건은 이곳 팔다에서!
http://palda.shop
- `
+ `;
const user = {
id: process.env.MAIL_ID,
password: process.env.MAIL_PASSWORD
-}
+};
const mailService = (toEmail, content, isSeller, isSold) => {
if (!toEmail) return;
// text html 택1 (html이 우선순위 높음)
const mail = {
to: toEmail,
- subject: `[팔다] 상품 ${content}이(가) ${mailTitleTemplate(isSeller, isSold)}`,
+ subject: `[팔다] 상품 ${content}이(가) ${mailTitleTemplate(
+ isSeller,
+ isSold
+ )}`,
html: mailTemplate(isSeller, isSold, content) + mailFooter
- }
+ };
const instance = axios.create({
baseURL: process.env.MAIL_BASE,
@@ -55,44 +64,49 @@ const mailService = (toEmail, content, isSeller, isSold) => {
"content-type": "application/json",
Accept: "application/json"
}
- })
+ });
- let cookie = ""
+ let cookie = "";
const getCookie = async () => {
- const response = await instance.post("/auth/login", user)
- cookie = response.headers["set-cookie"][0]
- }
+ const response = await instance.post("/auth/login", user);
+ cookie = response.headers["set-cookie"][0];
+ };
const sendMail = () =>
instance.post("/mail", mail, {
headers: {
Cookie: cookie
}
- })
+ });
const run = async () => {
try {
- await sendMail()
- return true
+ await sendMail();
+ return true;
} catch (error) {
if (error.response.status !== 401) {
- return error
+ return error;
}
}
- await getCookie()
- const result = await sendMail()
- }
+ await getCookie();
+ const result = await sendMail();
+ };
- run()
-}
+ run();
+};
const sendMail = (pool, userid, title, isSeller, isSold) => {
- pool.query('select email from users where id = ?', [userid],
+ pool.query(
+ "select email from users where id = ?",
+ [userid],
(err, row, field) => {
if (!row) return;
- mailService(row[0].email, title, isSeller, isSold)
- })
-}
+ if (!row[0]) return;
+ if (!row[0].email) return;
+ mailService(row[0].email, title, isSeller, isSold);
+ }
+ );
+};
-module.exports = { sendMail }
+module.exports = { sendMail };
diff --git a/chat-server/index.js b/chat-server/index.js
index 5d160490..ca0b9664 100644
--- a/chat-server/index.js
+++ b/chat-server/index.js
@@ -70,13 +70,17 @@ io.on("connection", function(socket) {
userMapper[userId] = sessionId;
});
- socket.on("leaveRoom", (roomId, sessionId, userId) => {
+ socket.on("leaveRoom", ({ roomId, sessionId, user }) => {
console.log(
- `##### USER(${sessionId}, ${userId})가 ${roomId}방에서 나감 #####`
+ `##### USER(${sessionId}, ${user.id})가 ${roomId}방에서 나감 #####`
);
socket.leave(roomId, () => {
//rooms 정보에서 삭제한다.
delete rooms[roomId][sessionId];
});
});
+
+ socket.on("disconnect", () => {
+ console.log("DISCONNECTED");
+ });
});
diff --git a/client/src/App.js b/client/src/App.js
index 522799a0..bb8ce2be 100644
--- a/client/src/App.js
+++ b/client/src/App.js
@@ -7,6 +7,8 @@ import ModalContext from "./context/ModalContext";
import { Modal } from "../src/components/Molecules/CustomModal/Modal";
import NotificationContext from "./context/NotificationContext";
import MessengerContext from "./context/MessengerContext";
+import SocketContext from "./context/SocketContext";
+import { useSocket } from "./hooks/useSocket";
function App() {
const [user, setUser] = useState({});
@@ -17,20 +19,23 @@ function App() {
});
const [notifications, setNotifications] = useState([]);
const [messengerOpen, setMessengerOpen] = useState(false);
+ const { socket } = useSocket(user, setNotifications);
return (
-
- {modal.isOpen ? : null}
-
-
-
-
+
+
+ {modal.isOpen ? : null}
+
+
+
+
+
diff --git a/client/src/components/Molecules/CustomModal/Modal.jsx b/client/src/components/Molecules/CustomModal/Modal.jsx
index f10f8375..de5be4d2 100644
--- a/client/src/components/Molecules/CustomModal/Modal.jsx
+++ b/client/src/components/Molecules/CustomModal/Modal.jsx
@@ -1,4 +1,4 @@
-import React, { useContext } from "react";
+import React, { useEffect, useContext } from "react";
import styled from "styled-components";
import ModalContext from "../../../context/ModalContext";
diff --git a/client/src/components/Molecules/NotifyList/NotifyItem.jsx b/client/src/components/Molecules/NotifyList/NotifyItem.jsx
index 970015dd..b7e7c7d7 100644
--- a/client/src/components/Molecules/NotifyList/NotifyItem.jsx
+++ b/client/src/components/Molecules/NotifyList/NotifyItem.jsx
@@ -10,7 +10,8 @@ const NotifyItemStyle = styled.div`
const NotifyBadge = styled.div`
width: 52px;
- background: ${props => (props.success ? "var(--color-success)" : "var(--color-primary)")};
+ background: ${props =>
+ props.success ? "var(--color-success)" : "var(--color-primary)"};
padding: var(--padding-sm);
color: white;
font-weight: bold;
@@ -22,7 +23,9 @@ const NotifyContent = styled.div`
width: 100%;
display: flex;
border: 1px solid var(--color-gray);
- /* border-color: ${props => (props.success ? "var(--color-success)" : "var(--color-primary)")}; */
+ background-color:white;
+ /* border-color: ${props =>
+ props.success ? "var(--color-success)" : "var(--color-primary)"}; */
`;
const NotifyLeftContent = styled.div`
@@ -84,9 +87,9 @@ export const NotifyItem = props => {
return (
{isSuccess(type) ? (
- 구매 성공
+ 성공
) : (
- 구매 실패
+ 실패
)}
diff --git a/client/src/components/Molecules/NotifyList/index.jsx b/client/src/components/Molecules/NotifyList/index.jsx
index b2b98b62..fd978162 100644
--- a/client/src/components/Molecules/NotifyList/index.jsx
+++ b/client/src/components/Molecules/NotifyList/index.jsx
@@ -31,7 +31,7 @@ function Component() {
{notifications.map(noti => {
return (
);
diff --git a/client/src/components/Molecules/ShareBox/index.jsx b/client/src/components/Molecules/ShareBox/index.jsx
index d3c37f3c..28f58ca0 100644
--- a/client/src/components/Molecules/ShareBox/index.jsx
+++ b/client/src/components/Molecules/ShareBox/index.jsx
@@ -109,7 +109,7 @@ const Component = ({ url, object, width }) => {
-
+ alert("URL이 복사되었습니다.")} />
URL
diff --git a/client/src/components/Organism/Chat/Chat.jsx b/client/src/components/Organism/Chat/Chat.jsx
index 92caec33..c894b225 100644
--- a/client/src/components/Organism/Chat/Chat.jsx
+++ b/client/src/components/Organism/Chat/Chat.jsx
@@ -30,7 +30,7 @@ const SellerIdText = styled.div`
`;
const MessageText = styled.div`
font-size: 0.8rem;
- word-break: break-all;
+ word-break: break-word;
`;
const Chat = ({ chat, isSeller }) => {
diff --git a/client/src/components/Organism/Chat/ChatBox.jsx b/client/src/components/Organism/Chat/ChatBox.jsx
index 16eeb950..c5f35646 100644
--- a/client/src/components/Organism/Chat/ChatBox.jsx
+++ b/client/src/components/Organism/Chat/ChatBox.jsx
@@ -5,6 +5,7 @@ import ChatSend from "./ChatSend";
import ModalContext from "../../../context/ModalContext";
import FailModal from "../../Molecules/CustomModal/FailModal";
import ProductPageContext from "../../../context/ProductPageContext";
+import SocketContext from "../../../context/SocketContext";
const ChatBoxStyle = styled.div`
display: flex;
@@ -61,8 +62,9 @@ const ChatAlertWithPurchase = styled(ChatAlertWithBid)`
const ChatBox = ({ productId, sellerId, user }) => {
const chatBodyRef = useRef();
+ const { socket } = useContext(SocketContext);
const [productPageState] = useContext(ProductPageContext);
- const { socketClient, chats } = productPageState;
+ const { chats } = productPageState;
const [modal, setModal] = useContext(ModalContext);
@@ -73,6 +75,10 @@ const ChatBox = ({ productId, sellerId, user }) => {
const onSubmit = e => {
e.preventDefault();
+ const text = e.target.message.value;
+
+ if (text === "" || text.trim() === "") return;
+
if (Object.keys(user).length === 0) {
return setModal({
isOpen: true,
@@ -80,11 +86,11 @@ const ChatBox = ({ productId, sellerId, user }) => {
props: { message: "로그인이 필요합니다." }
});
}
- socketClient.emit("message", {
+ socket.emit("message", {
roomId: productId,
- sender: { ...user, sessionId: socketClient.id },
+ sender: { ...user, sessionId: socket.id },
type: "message",
- text: e.target.message.value,
+ text,
createdAt: Date.now()
});
@@ -98,7 +104,11 @@ const ChatBox = ({ productId, sellerId, user }) => {
{chats.map(chat => {
return chat.type === "message" ? (
-
+
) : (
{chat.type === "bid" ? (
diff --git a/client/src/components/Organism/ProductInfo/index.jsx b/client/src/components/Organism/ProductInfo/index.jsx
index a4872cc7..7e1f1468 100644
--- a/client/src/components/Organism/ProductInfo/index.jsx
+++ b/client/src/components/Organism/ProductInfo/index.jsx
@@ -16,6 +16,7 @@ import ReportButton from "../../Atoms/ReportButton";
import MessengerCreateButton from "../../Messenger/CreateButton";
import Carousel from "../../Molecules/Carousel";
import { getNowDateTime, isTerminated } from "../../../utils/dateUtil";
+import SocketContext from "../../../context/SocketContext";
const { apiUrl } = apiConfig;
@@ -195,11 +196,11 @@ const ShareWrapper = styled.div`
const ProductInfo = () => {
const [user] = useContext(UserContext);
-
+ const { socket } = useContext(SocketContext);
const [productPageState, dispatchProductPage] = useContext(
ProductPageContext
);
- const { socketClient, product, chats, bids } = productPageState;
+ const { product, chats, bids } = productPageState;
const [modal, setModal] = useContext(ModalContext);
/*
@@ -298,7 +299,7 @@ const ProductInfo = () => {
axios
.post(`${baseURL}${pathConfig.bids}`, params)
.then(response => {
- socketClient.emit("bid", {
+ socket.emit("bid", {
type: "bid",
roomId: id,
sender: { ...user, sessionId: user.sessionId },
@@ -366,9 +367,9 @@ const ProductInfo = () => {
axios
.patch(`${baseURL}${pathConfig.products}/${id}`, params)
.then(response => {
- socketClient.emit("purchase", {
+ socket.emit("purchase", {
roomId: id,
- sender: { ...user, sessionId: socketClient.id },
+ sender: { ...user, sessionId: socket.id },
sold: response.data,
createdAt: getNowDateTime()
});
diff --git a/client/src/context/SocketContext.js b/client/src/context/SocketContext.js
new file mode 100644
index 00000000..96ab088c
--- /dev/null
+++ b/client/src/context/SocketContext.js
@@ -0,0 +1,5 @@
+import React from "react";
+
+const SocketContext = React.createContext();
+
+export default SocketContext;
diff --git a/client/src/hooks/useSocket.js b/client/src/hooks/useSocket.js
new file mode 100644
index 00000000..022f13a7
--- /dev/null
+++ b/client/src/hooks/useSocket.js
@@ -0,0 +1,29 @@
+import React, { useState, useEffect } from "react";
+import io from "socket.io-client";
+import apiConfig from "../config/api";
+
+const { chatUrl } = apiConfig;
+
+export const useSocket = (user, setNotifications) => {
+ const [socket, setSocket] = useState(null);
+
+ useEffect(() => {
+ if (Object.keys(user).length === 0) return;
+
+ const socket = io(chatUrl);
+
+ socket.on("connect", () => {
+ setSocket(socket);
+ });
+
+ socket.on("auctionResult", ({ type, product }) => {
+ setNotifications(notis => [...notis, { type, product }]);
+ });
+
+ return () => {
+ socket.close();
+ };
+ }, [user]);
+
+ return { socket };
+};
diff --git a/client/src/pages/Product/index.jsx b/client/src/pages/Product/index.jsx
index 65b7bf3c..be473ced 100644
--- a/client/src/pages/Product/index.jsx
+++ b/client/src/pages/Product/index.jsx
@@ -8,15 +8,15 @@ import { useFetch } from "../../hooks/useFetch";
import pathConfig from "../../config/path";
import UserContext from "../../context/UserContext";
import apiConfig from "../../config/api";
-import io from "socket.io-client";
import ProductPageContext from "../../context/ProductPageContext";
import { convert2Price } from "../../utils/converter";
import NotificationContext from "../../context/NotificationContext";
import { getFetch } from "../../services/fetchService";
import SmallCardContainer from "../../components/Molecules/SmallCardContainer";
import ErrorPage from "../../pages/ErrorPage";
+import SocketContext from "../../context/SocketContext";
-const { chatUrl, apiUrl } = apiConfig;
+const { apiUrl } = apiConfig;
const ProductPageStyle = styled.div`
display: flex;
@@ -48,8 +48,7 @@ const initialProductPageState = {
loading: true,
product: {},
bids: [],
- chats: [],
- socketClient: null
+ chats: []
};
const productPageReducer = (state, action) => {
@@ -80,8 +79,6 @@ const productPageReducer = (state, action) => {
...state,
chats: [...state.chats, action.chat]
};
- case "SET_SOCKET":
- return { ...state, socketClient: action.socket };
case "UPDATE_PRODUCT":
return { ...state, product: { ...state.product, ...action.product } };
default:
@@ -89,16 +86,17 @@ const productPageReducer = (state, action) => {
}
};
-const DEFAULT_PROFILE_URL = "https://kr.object.ncloudstorage.com/palda/img/default-profile-img.jpg";
+const DEFAULT_PROFILE_URL =
+ "https://kr.object.ncloudstorage.com/palda/img/default-profile-img.jpg";
const ProductPage = ({ match }) => {
+ const [user] = useContext(UserContext);
+ const { socket } = useContext(SocketContext);
+ const [, setNotifications] = useContext(NotificationContext);
const [productPageState, dispatchProductPage] = useReducer(
productPageReducer,
initialProductPageState
);
-
- const [user] = useContext(UserContext);
- const [notifications, setNotifications] = useContext(NotificationContext);
const [relatedItemList, setRelatedItemList] = useState([]);
const productId = match.params.id;
@@ -116,29 +114,19 @@ const ProductPage = ({ match }) => {
dispatchProductPage({ type: "FETCH_ERROR", error });
};
- useFetch(`${pathConfig.productsWithBids}/${productId}`, handleFetchSuccess, handleFetchError);
-
- const getRelatedItemList = async () => {
- if (!productPageState.loading) {
- setRelatedItemList([]);
- const { categoryCode, id } = productPageState.product;
- const url = `${apiUrl}${pathConfig.items.related}/${categoryCode}/${id}`;
- let result = await getFetch(url, {}, {});
-
- setRelatedItemList(result[0]);
- }
- };
+ useFetch(
+ `${pathConfig.productsWithBids}/${productId}`,
+ handleFetchSuccess,
+ handleFetchError
+ );
useEffect(() => {
- if (Object.keys(user).length === 0) return;
- const socket = io(chatUrl);
- socket.on("connect", () => {
- dispatchProductPage({ type: "SET_SOCKET", socket });
- socket.emit("joinRoom", {
- roomId: productId,
- sessionId: socket.id,
- user: user
- });
+ if (!socket) return;
+
+ socket.emit("joinRoom", {
+ roomId: productId,
+ sessionId: socket.id,
+ user: user
});
socket.on("message", ({ roomId, sender, type, text, createdAt }) => {
@@ -160,7 +148,9 @@ const ProductPage = ({ match }) => {
sessionId: sender.sessionId,
id: sender.loginId,
src: sender.profileUrl || DEFAULT_PROFILE_URL,
- text: `${sender.name}님께서 ${convert2Price(bid.bidPrice)}원에 입찰 하셨습니다.`,
+ text: `${sender.name}님께서 ${convert2Price(
+ bid.bidPrice
+ )}원에 입찰 하셨습니다.`,
key: `B.${createdAt}.${sender.id}`
};
@@ -173,7 +163,9 @@ const ProductPage = ({ match }) => {
sessionId: sender.sessionId,
id: sender.loginId,
src: sender.profileUrl || DEFAULT_PROFILE_URL,
- text: `${sender.name}님이 ${convert2Price(sold.soldPrice)}원에 즉시 구매하셨습니다.`,
+ text: `${sender.name}님이 ${convert2Price(
+ sold.soldPrice
+ )}원에 즉시 구매하셨습니다.`,
key: `P.${createdAt}.${sender.id}`
};
@@ -182,22 +174,25 @@ const ProductPage = ({ match }) => {
return dispatchProductPage({ type: "ADD_PURCHASE", chat, product });
});
- socket.on("auctionResult", ({ type, product }) => {
- setNotifications(notis => [...notis, { type, product }]);
- });
-
- socket.on("joinRoom", message => {
- // console.log(message);
- });
-
- socket.on("disconnect", reason => {
- // console.log(reason);
- });
-
return () => {
- socket.close();
+ socket.emit("leaveRoom", {
+ roomId: productId,
+ sessionId: socket.id,
+ user: user
+ });
};
- }, [user, chatUrl, dispatchProductPage]);
+ }, [socket]);
+
+ const getRelatedItemList = async () => {
+ if (!productPageState.loading) {
+ setRelatedItemList([]);
+ const { categoryCode, id } = productPageState.product;
+ const url = `${apiUrl}${pathConfig.items.related}/${categoryCode}/${id}`;
+ let result = await getFetch(url, {}, {});
+
+ setRelatedItemList(result[0]);
+ }
+ };
useEffect(() => {
getRelatedItemList();
@@ -206,11 +201,15 @@ const ProductPage = ({ match }) => {
if (productPageState.error) {
return ;
}
- const sellerId = productPageState.product.seller ? productPageState.product.seller.id : undefined;
+ const sellerId = productPageState.product.seller
+ ? productPageState.product.seller.id
+ : undefined;
return productPageState.loading ? (
) : (
-
+
@@ -222,11 +221,19 @@ const ProductPage = ({ match }) => {
) : null}
-
+