parksh93 / GOALDDAE_FRONT

ํ”„๋กœ์ ํŠธ GOALDDAE ํ”„๋ก ํŠธ์—”๋“œ ๋ ˆํผ์ง€ํ† ๋ฆฌ
3 stars 0 forks source link

Project Detail


1. ๋™์ ํ…Œ์ด๋ธ”

์ €ํฌ๋Š” ์ •์ ํ…Œ์ด๋ธ”๊ณผ ๋™์ ํ…Œ์ด๋ธ”์„ ๋ชจ๋‘ ํ™œ์šฉํ•˜์—ฌ DB๋ฅผ ๊ตฌ์„ฑํ–ˆ์Šต๋‹ˆ๋‹ค. ๋™์ ํ…Œ์ด๋ธ”์„ ์‚ฌ์šฉํ•œ ์ด์œ 
๊ฒŒ์‹œํŒ์„ ์˜ˆ๋กœ ๋“ค์–ด ๊ฐ ๊ฒŒ์‹œ๊ธ€๋ณ„ ๋Œ“๊ธ€์„ ํ•œ๋ฒˆ์— ์ €์žฅํ• ๊ฒฝ์šฐ ํ•˜๋‚˜์˜ ๊ธ€์˜ ๋Œ“๊ธ€์„ ์กฐํšŒํ•ด์•ผ ํ•  ๋•Œ ์ •์  ํ…Œ์ด๋ธ”๋กœ ๋ชจ๋“  ๋Œ“๊ธ€์„ ํ•œ๋ฒˆ์— ์ €์žฅ ์‹œ ์กฐํšŒ ์ฒ˜๋ฆฌ์— ์‹œ๊ฐ„์ด ๊ฑธ๋ฆด ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๊ทธ๋ ‡๊ธฐ ๋•Œ๋ฌธ์— ๊ฐ ๊ฒŒ์‹œ๊ธ€๋ณ„๋กœ ๋Œ“๊ธ€ ๋™์ ํ…Œ์ด๋ธ”์„ ๊ตฌ์„ฑํ•ด ์กฐํšŒ ์‹œ๊ฐ„์„ ๋‹จ์ถ•ํ•˜๊ธฐ ์œ„ํ•ด ๋™์  ํ…Œ์ด๋ธ”์„ ์‚ฌ์šฉํ–ˆ์Šต๋‹ˆ๋‹ค. ๋˜ํ•œ, ๋™์  ํ…Œ์ด๋ธ” ์‚ฌ์šฉ์‹œ MyBatis๋ฅผ ์ด์šฉํ•˜๊ฒŒ ๋˜๋Š”๋ฐ ์ด๋•Œ SQL Injection ๋ฌธ์ œ๋ฅผ ๋ณด์™„ํ•˜๊ธฐ ์œ„ํ•ด ` $ ` ๋Œ€์‹  ` # ` ์„ ์‚ฌ์šฉํ•ด ์‚ฌ์šฉ์ž๊ฐ€ ์ฟผ๋ฆฌ๋ฅผ ๋‚ ๋ฆฌ๋”๋ผ๋„ ๋ฌธ์ œ๊ฐ€ ๋ฐœ์ƒํ•˜์ง€ ์•Š๋„๋ก ์ฒ˜๋ฆฌํ–ˆ์Šต๋‹ˆ๋‹ค.


2. Spring Security

JWT ํ† ํฐ์„ ์ด์šฉํ•ด ์‚ฌ์šฉ์ž๊ฐ€ ๋กœ๊ทธ์ธ ํ–ˆ์„ ๋•Œ ํ•ด๋‹น ์‚ฌ์šฉ์ž์˜ ๊ถŒํ•œ์ด ๋‹ด๊ฒจ ์žˆ๋Š” JWT ํ† ํฐ์„ ๋ฐœ๊ธ‰ํ•ด ๊ถŒํ•œ์— ๋”ฐ๋ผ ์„œ๋น„์Šค ์ด์šฉ ์ œํ•œ
๋˜ํ•œ, ํ•ด๋‹น JWT ํ† ํฐ์„ ์ฟ ํ‚ค์— HTTPOnly๋กœ ์ €์žฅํ•ด XSS ๊ณต๊ฒฉ์— ๋Œ€๋น„ํ–ˆ์Šต๋‹ˆ๋‹ค. Cookie cookie = new Cookie("token", token); cookie.setHttpOnly(true); cookie.setPath("/"); // ๋ชจ๋“  ํŽ˜์ด์ง€์—์„œ ์ ‘๊ทผ ๊ฐ€๋Šฅ Refresh Token ์‚ฌ์šฉ
Access Token์˜ ์œ ํšจ๊ธฐ๊ฐ„์„ ์งง๊ฒŒ ๋‘์–ด Access Token์˜ ํƒˆ์ทจ๋ฅผ ์ตœ์†Œํ™”ํ–ˆ์Šต๋‹ˆ๋‹ค. Access Token์ด ๋งŒ๋ฃŒ๋˜์—ˆ์„ ๊ฒฝ์šฐ ์‚ฌ์šฉ์ž์˜ ์žฌ๋กœ๊ทธ์ธ์„ ์ตœ์†Œํ™” ํ•˜๊ธฐ ์œ„ํ•ด Refresh Token์„ DB์— ์ €์žฅํ•ด ๋‘๊ณ  ๋งŒ๋ฃŒ ์‹œ Refresh Token์„ ํ†ตํ•ด Access Token์„ ์žฌ๋ฐœ๊ธ‰ํ•ด์ฃผ๋„๋ก ์„ค์ •ํ–ˆ์Šต๋‹ˆ๋‹ค. if(!token.equals("")) { if (tokenProvider.validToken(token)) { Authentication authentication = tokenProvider.getAuthentication(token); SecurityContextHolder.getContext().setAuthentication(authentication); } else { if(!refreshToken.equals("")){ Long userId = tokenProvider.getUserId(refreshToken); RefreshToken refreshTokenEntity = refreshTokenService.findByUserId(userId); boolean validRefreshToken = tokenProvider.validToken(refreshTokenEntity.getRefreshToken()); if (validRefreshToken && refreshToken.equals(refreshTokenEntity.getRefreshToken())) { User user = userJPARepository.findById(userId).get(); String newAccessToken = tokenProvider.generateToken(user, ACCESS_TOKEN_DURATION); String newRefreshToken = tokenProvider.generateToken(user, REFRESH_TOKEN_DURATION); refreshTokenService.saveRefreshToken(userId, newRefreshToken); CookieUtil.addCookie(response, ACCESS_TOKEN_COOKIE_NAME, newAccessToken); CookieUtil.addCookie(response, REFRESH_TOKEN_COOKIE_NAME, newRefreshToken); Authentication authentication = tokenProvider.getAuthentication(newAccessToken); SecurityContextHolder.getContext().setAuthentication(authentication); } } } }


3. WebSocket

์ฑ„ํŒ… ๋ฐ ์นœ๊ตฌ ๊ธฐ๋Šฅ ์‚ฌ์šฉ์‹œ ์‹ค์‹œ๊ฐ„ ๋ฐ˜์˜์„ ์œ„ํ•ด WebSocket์„ ์‚ฌ์šฉํ–ˆ์Šต๋‹ˆ๋‹ค. ํ•ด๋‹น ๊ธฐ๋Šฅ๋“ค์€ 1๋Œ€1 ํ†ต์‹ ์ด๊ธฐ ๋–„๋ฌธ์— queue ๋ฐฉ์‹์„ ์ด์šฉํ•ด ๊ตฌํ˜„ํ–ˆ์Šต๋‹ˆ๋‹ค. @Configuration @EnableWebSocketMessageBroker public class WebSocketConfig implements WebSocketMessageBrokerConfigurer { @Override public void registerStompEndpoints(StompEndpointRegistry registry) { registry.addEndpoint("/friend").setAllowedOrigins("http://localhost:3000").withSockJS(); } @Override public void configureMessageBroker(MessageBrokerRegistry registry) { registry.enableSimpleBroker("/queue"); registry.setApplicationDestinationPrefixes("/app"); } } --- @RestController public class FriendWebSocketController { private static Set userList = new HashSet<>(); private SimpMessagingTemplate simpMessagingTemplate; @Autowired public FriendWebSocketController(SimpMessagingTemplate simpMessagingTemplate){ this.simpMessagingTemplate = simpMessagingTemplate; } @MessageMapping("/friend/{id}") public void sendMessage(@Payload FriendSocketMsgDTO friendSocketMsgDTO, @DestinationVariable Integer id){ this.simpMessagingTemplate.convertAndSend("/queue/FriendRequestToClient/"+ id, friendSocketMsgDTO); } @MessageMapping("/friend/join") public void joinUser(@Payload Integer userId){ userList.add(userId); } } EndPoint๋ฅผ ์ง€์ •ํ•ด ํ•ด๋‹น EndPoint๋กœ ์š”์ฒญ์„ ๋ฐ›์Šต๋‹ˆ๋‹ค.
` join ` ์„ ํ†ตํ•ด Socket ์„œ๋ฒ„์™€ ์—ฐ๊ฒฐํ•œ ํ›„ ์—ฐ๊ฒฐ๋œ ์‚ฌ์šฉ์ž๋กœ ๋ถ€ํ„ฐ ๋ฉ”์‹œ์ง€๋ฅผ ๋ฐ›์•„ ์‘๋‹ต์„ ๋ฐ›์„ ์‚ฌ์šฉ์ž์—๊ฒŒ ๋ฉ”์‹œ์ง€๋ฅผ ์ „์†กํ•ฉ๋‹ˆ๋‹ค.


4. Crawling

์ €ํฌ๋Š” ๋„ค์ด๋ฒ„ ํ•ด์™ธ ์ถ•๊ตฌ ๊ธฐ์‚ฌ๋ฅผ ํฌ๋กค๋งํ•ด ์‚ฌ์šฉ์ž์—๊ฒŒ ํ˜„์žฌ์˜ ํ•ซ์ด์Šˆ๋ฅผ ๋ณด์—ฌ์ฃผ๊ณ ์ž ํ•˜์˜€์Šต๋‹ˆ๋‹ค.
์‚ฌ์šฉ์ž๋Š” ๊ธฐ์‚ฌ๋ฅผ 5๊ฐœ์”ฉ ๋ณผ ์ˆ˜ ์žˆ์œผ๋ฉฐ, 15์ดˆ ๋งˆ๋‹ค ์ž๋™์œผ๋กœ ๋„˜์–ด๊ฐ€๋ฉฐ ์ด 25๊ฐœ์˜ ๊ธฐ์‚ฌ๋ฅผ ๋ณด์—ฌ์ค๋‹ˆ๋‹ค. ๋ฐ์ดํ„ฐ๋Š” 3์ผ ์ด์ƒ์ด ๋˜๋ฉด ์ž๋™์œผ๋กœ ์‚ญ์ œ๋˜๊ฒŒ๋” ํ•˜์˜€๊ณ , ์ค‘์ ์ ์œผ๋กœ ์ƒ๊ฐํ•œ ๋ถ€๋ถ„์€ ์ถ•๊ตฌ๋Š” ์ตœ์‹  ๊ธฐ์‚ฌ์— ๋ฏผ๊ฐํ•˜๋ฏ€๋กœ ` @Scheduled ` ์„ ์ด์šฉํ•˜์—ฌ ํ•˜๋ฃจ์— 4๋ฒˆ 6์‹œ, 12์‹œ, 18์‹œ, 24์‹œ ํฌ๋กค๋ง๋˜๊ฒŒ๋” ์ ์šฉํ•˜์˜€์Šต๋‹ˆ๋‹ค. ๊ทธ๋ฆฌ๊ณ  DB์—๋Š” ์ƒ์„ฑ ์ผ์ž ๋‚ด๋ฆผ์ฐจ์ˆœ์œผ๋กœ ์ €์žฅ๋˜๋ฉฐ, ๋‚ด๋ฆผ์ฐจ์ˆœ์œผ๋กœ ๋…ธ์ถœ๋˜๊ฒŒ ๊ตฌํ˜„ํ–ˆ์Šต๋‹ˆ๋‹ค.


5. CI/CD

Jenkins์™€ Docker๋ฅผ ์ด์šฉํ•ด ๋ฐฐํฌ๋ฅผ ์ง„ํ–‰ํ–ˆ์Šต๋‹ˆ๋‹ค. ๊นƒํ—ˆ๋ธŒ๋ฅผ ํ†ตํ•ด ์ด๋ฒคํŠธ๊ฐ€ ๋ฐœ์ƒํ•˜๊ฒŒ ๋˜๋ฉด ์›นํ›…์„ ํ†ตํ•ด Jenkins์—์„œ ์š”์ฒญ์„ ์บ์น˜ํ•ด ๋„์ปค ์ด๋ฏธ์ง€๋ฅผ ๋นŒ๋“œํ•ฉ๋‹ˆ๋‹ค. ๋นŒ๋“œ๊ฐ€ ์™„๋ฃŒ๋˜๋ฉด ๋นŒ๋“œ๋œ ์ด๋ฏธ์ง€๋ฅผ DockerHub๋กœ pushํ•ด ๊ฐ ์„œ๋ฒ„์—์„œ ํ•ด๋‹น ์ด๋ฏธ์ง€๋ฅผ pull ๋ฐ›์•„ ์„œ๋ฒ„๋ฅผ ์‹คํ–‰ํ•ฉ๋‹ˆ๋‹ค. ์‚ฌ์šฉ์ž์˜ ์š”์ฒญ์ด ๋“ค์–ด์™”์„๋•Œ ๋ฐ”๋กœ ์„œ๋ฒ„๋กœ ์š”์ฒญ์„ ๋ณด๋‚ด๋Š” ๊ฒƒ์ด ์•„๋‹Œ nginx๋ฅผ ๊ฑฐ์ณ ํ—ฌ์Šค ์ฒดํฌ๋ฅผ ํ†ตํ•ด ๋กœ๋“œ๋ฐธ๋Ÿฐ์‹ฑ ์ฒ˜๋ฆฌํ•˜์—ฌ ํŠธ๋ž˜ํ”ฝ์„ ๋ถ„์‚ฐ ์‹œ์ผฐ์Šต๋‹ˆ๋‹ค. ๋˜ํ•œ, ๋ฒ„์ „ ์—…์œผ๋กœ ์ธํ•ด ์ƒˆ๋กญ๊ฒŒ ๋ฐฐํฌ๋ฅผ ์ง„ํ–‰ํ•ด์•ผ ํ• ๋•Œ ์„œ๋ฒ„์˜ ์ค‘๋‹จ์„ ๋ง‰๊ธฐ ์œ„ํ•ด ๋กค๋ง ๋ฐฉ์‹์„ ๋„์ž…ํ•ด ๋ฌด์ค‘๋‹จ ๋ฐฐํฌ๋ฅผ ์ˆ˜ํ–‰ํ–ˆ์Šต๋‹ˆ๋‹ค.

Tech & Tool

---

Backend

Frontend


DB

Server & CI/CD


Tools