Gallery
Search

[Boot+JPA] ์๊ตฌ์ฌํญ ๋ถ์ ๋ฐ ์ค๊ณ

[Boot+JPA] JPA, DB ์ค์

[Boot+JPA] ํ๋ก์ ํธ ํ๊ฒฝ ์ค์

[SpringCore] ๋น ์ค์ฝํ - ์น ์ค์ฝํ

[SpringCore] ๋น ์ค์ฝํ - ์ฑ๊ธํค๊ณผ ํ๋กํ ํ์

[SpringCore] ๋น ์๋ช
์ฃผ๊ธฐ ์ฝ๋ฐฑ (์ฌ๋ฐ๋ฅธ ์ด๊ธฐํ, ์ข
๋ฃ ๋ฐฉ๋ฒ)

[SpringCore] ์๋์ฃผ์
, ์๋์ฃผ์
์ฌ๋ฐ๋ฅธ ์ค๋ฌด ์ด์ ๊ธฐ์ค

[SpringCore] ์กฐํํ ๋น์ด ๋ชจ๋ ํ์ํ ๋(๋์ ์ผ๋ก ์ ํ์ด ํ์ํ ๋)

[SpringCore] @Qulifier ์ถ๊ฐ ํ์, @Primary ์ฐ์ ๊ถ ์ง์ , ์ ๋ํ
์ด์
์ ์ง์ ๋ง๋ค๊ธฐ

[SpringCore] ๋กฌ๋ณต์ ํ์ฉํ ๋ฆฌํฉํ ๋ง

[SpringCore] ์์กด๊ด๊ณ ์๋ ์ฃผ์
๋ฐฉ๋ฒ๋ค๊ณผ ์์ฑ์ ์ฃผ์
์ ๊ถ์ฅํ๋ ์ด์

[SpringCore] ์ปดํฌ๋ํธ ์ค์บ - ์ค์บ ๋ฒ์ ์ง์ , ๊ธฐ๋ณธ ์ค์บ ๋์

[SpringCore] ์ปดํฌ๋ํธ ์ค์บ - ์คํ๋ง ๋น ์๋ โ ์๋ ์ฃผ์
๋ณ๊ฒฝ

[SpringCore] ์ฑ๊ธํค ํจํด - @Configuration์ ์ฑ๊ธํค ํ
์คํธ

[SpringCore] ์ฑ๊ธํค ํจํด - ์ฑ๊ธํค ๋ฐฉ์์ ์ฃผ์์ (stateless ์ค๊ณ ํ์์ฑ)

[SpringCore] ์ฑ๊ธํค ํจํด - ์ฑ๊ธํค ํจํด์ ์ฌ์ฉํ๋ ์ด์ ์ ์คํ๋ง ์ปจํ
์ด๋์ ์ฑ๊ธํค ํจํด

[SpringCore] ์คํ๋ง ์ปจํ
์ด๋์ ์คํ๋ง ๋น - ์คํ๋ง ์ปจํ
์ด๋์ ์ค์ ํ์ ์ง์ (Java ์ด๋
ธํ
์ด์
/XML)๊ณผ BeanDefinition

[SpringCore] ์คํ๋ง ์ปจํ
์ด๋์ ์คํ๋ง ๋น - BeanFactory์ ApplicationContext

[SpringCore] ์คํ๋ง ์ปจํ
์ด๋์ ์คํ๋ง ๋น - ์คํ๋ง ์ปจํ
์ด๋์ ๋ฑ๋ก๋ ๋น ์กฐํ ํ
์คํธ

[SpringCore] ์คํ๋ง ์ปจํ
์ด๋์ ์คํ๋ง ๋น - ๊ธฐ์กด DI ์๋ฐ ์ฝ๋๋ฅผ Spring ์ปจํ
์ด๋ ํ์ฉ ์ฝ๋๋ก ๋ฆฌํฉํ ๋ง

[SpringCore] ๊ฐ์ฒด ์งํฅ ์๋ฆฌ ์ ์ฉ - ์ญํ , ๊ตฌํ์ ๋ถ๋ฆฌ๋ฅผ ์ํ AppConfig์ ์ถ๊ฐ, ์ ์ด์ ์ญ์ (IoC), ์์กด๊ด๊ณ ์ฃผ์
(DI)

[SpringIntro] AOP ํ์ฉ

[SpringIntro] ์คํ๋ง DB ์ ๊ทผ ๊ธฐ์ - 3, Spring Data JPA

[SpringIntro] ์คํ๋ง DB ์ ๊ทผ ๊ธฐ์ - 2, JPA

[SpringIntro] ์คํ๋ง DB ์ ๊ทผ ๊ธฐ์ - 1, JDBC์ JDBC Template

[Spring-LDW] ์คํ๋ง ๋ถํธ ์ฑ
ํ๊ถ ๋๋ด๊ธฐ ๋ชฉํ

[Spring][iLanD] [๋น๋] Jar ๋น๋ ์ดํ ์คํ ์ ํ์๋ฆฌํ ํธ๋ฌ๋ธ์ํ
[Spring][iLanD] [ํ๋ก์ ํธ์๋ฃ] ๊ฐ ๊ธฐ๋ฅ ๊ตฌํ ์์ฐ

[Spring][iLanD] [ํ๋ก์ ํธ์๋ฃ] ํOS ์คํํ๊ฒฝ ๊ตฌ์ถ๊ฐ์ด๋

[Spring][iLanD] [Mini Game] ๊ฐ์๋ฐ์๋ณด ๊ฒ์ ๊ธฐ๋ฅ ๋ฃ๊ธฐ

[Spring][iLanD] [Base64] Video ํ์ผ ์ถ๊ฐ, Img ํฌ์คํฐ์ ๋ถ๋ฆฌ, Video ํ์ผ ์ฒ๋ฆฌ ๋ก์ง ๋ถ๋ฆฌ, ์ธ์ฝ๋ฉ ํ ์ด๋ฏธ์ง ์ถ๋ ฅ, Video ํ๊ทธ๋ฅผ ํตํ ์์ ์ฌ์
์ค์๊ฐ ์ฑํ
๊ธฐ๋ฅ์ด ์ด๋์ ๋ ๊ตฌํ๋์์ผ๋ ๋ค๋ฌ๊ธฐ๋ฅผ ๋ค์ด๊ฐ๋ค. ย
์ํ๋๊ฑด ์ฑํ
์ ๊ตฌ๋ถ์ด ์๋๋๊ฒ๊ณผ ์๋ด์ ๊ฐ์ ๋ถ๋ถ์ด ๋ถ์กฑํ๋ค.
์นดํก๊ณผ ๋น์ทํ๊ฒ ๊ตฌํํด๋ณด๊ณ ์ถ๋ค. ๊ตฌํ๋ ๋ชจ์ต์ ์ฐ์ ๋ค์๊ณผ ๊ฐ๋ค. ๋ค๋ฅธ ๋ธ๋ผ์ฐ์ ๋ฅผ ํตํด์ ๋ก๊ทธ์ธ๋ ์ ์ ์ ๋ก๊ทธ์ธ๋์ง ์์ ์ ์ ์ ์ํฉ์ ์ฐ์ถํ๋ค.
แแ
ฐแธแแ
ฉแแ
ฆแบ แแ
ฆแแ
ตแซแแ
ฅ แแ
ฎแแ
งแซ.mp4
chatting.js
์ ๋ฐ์ ์ผ๋ก ์ฝ๋๊ฐ ๋ณต์กํด์ง๊ธดํ๋ค. ๋ก๊ทธ์ธ์ ์์ฑ๋๋ ํด๋ผ์ด์ธํธ ์ฟ ํค๋ก๋ถํฐ ์ฌ์ฉ์ ์ด๋ฆ์ ๋ฐ์์ค๋๋กํ๋ค. ๋ค๋ฅธ ๋ฐฉ๋ฒ๋ค๋ก user์ Authentication์ ๋ฐ์์ค๋ ๊ฒ์ ๊ตฌํํด๋ณด๊ณ ์ถ๋ค.
const client = new StompJs.Client({
brokerURL: 'ws://192.168.0.2:8080/websocket'
// ๋ธ๋ก์ปค์ฃผ์๋ฅผ ์๋ฒ IP๋ก ์์ ํด์ผํ๋ค.
// brokerURL: 'ws://localhost:8080/websocket'
});
client.onConnect = (frame) => {
setConnected(true);
console.log('Connected: ' + frame);
client.subscribe('/chatMessage', (chatting) => {
showChatting(JSON.parse(chatting.body).content);
});
};
client.onWebSocketError = (error) => {
console.error('Error with websocket', error);
};
client.onStompError = (frame) => {
console.error('Broker reported error: ' + frame.headers['message']);
console.error('Additional details: ' + frame.body);
};
function setConnected(connected) {
$("#chatConnect").prop("disabled", connected);
$("#chatDisconnect").prop("disabled", !connected);
$("#message").html("");
// ์ฐ๊ฒฐ ์ํ์ ๋ฐ๋ผ ๋ฉ์์ง ์ถ๊ฐ
var message = connected ? "[System] ์ฑํ
๋ฐฉ์ ์ฐ๊ฒฐ๋์์ต๋๋ค." : "[System] ์ฑํ
๋ฐฉ์์ ๋๊ฐ์ต๋๋ค.";
showChatting(message);
}
function connect() {
client.activate();
}
function disconnect() {
client.deactivate();
setConnected(false);
console.log("Disconnected");
}
// ํด๋ผ์ด์ธํธ์์ ํ ํฐ์ ์ถ์ถํ๋ ํจ์ ์ ์
function extractUsernameFromToken(token) {
try {
const base64Url = token.split('.')[1];
const base64 = base64Url.replace(/-/g, '+').replace(/_/g, '/');
const jsonPayload = decodeURIComponent(atob(base64).split('').map(function(c) {
return '%' + ('00' + c.charCodeAt(0).toString(16)).slice(-2);
}).join(''));
const payload = JSON.parse(jsonPayload);
return payload.sub; // ํ ํฐ์์ ์ ์ ์ด๋ฆ ์ถ์ถ
} catch (error) {
console.error('Error extracting username from token:', error);
return null;
}
}
// ํ ํฐ ๊ฐ์ ธ์ค๊ธฐ ํจ์ ์ ์
function getJwtToken() {
// ์ฌ๊ธฐ์์ ์ค์ ๋ก ํ ํฐ์ ๊ฐ์ ธ์ค๋ ๋ก์ง์ ์์ฑํด์ผ ํจ
// ์์๋ก ์ฟ ํค์์ ํ ํฐ์ ๊ฐ์ ธ์ค๋ ๋ฐฉ์ ์ฌ์ฉ
const name = "Authorization=";
const decodedCookie = decodeURIComponent(document.cookie);
const cookieArray = decodedCookie.split(';');
for (let i = 0; i < cookieArray.length; i++) {
let cookie = cookieArray[i].trim();
if (cookie.indexOf(name) === 0) {
return cookie.substring(name.length, cookie.length);
}
}
return null;
}
// ํ ํฐ์ด ์์ ๊ฒฝ์ฐ "[Unknown]" ๋ฐํํ๋ ํจ์
function getUsernameOrUnknown() {
const token = getJwtToken();
return token ? extractUsernameFromToken(token) : "Unknown";
}
// ์ฑํ
๋ฉ์์ง ์ ์ก ์ ์ ์ ์ด๋ฆ์ ํจ๊ป ์ ์ก
function sendMessage() {
const chattingMessage = $("#chattingMessage").val();
const username = getUsernameOrUnknown(); // ์์ : getUsernameOrUnknown ์ฌ์ฉ
if (username) {
const messageToSend = `[${username}] ${chattingMessage}`;
// ์ ์กํ ๋ฉ์์ง ๊ตฌ์ฑ
client.publish({
destination: "/app/chat",
body: JSON.stringify({'chattingMessage': messageToSend})
});
// ํ
์คํธ ์
๋ ฅ ๋ถ๋ถ์ผ๋ก ์ปค์ ์ด๋ ๋ฐ ๋ด์ฉ ๋น์ฐ๊ธฐ
$("#chattingMessage").val('').focus();
} else {
console.error('Username not available.');
}
}
function showChatting(message) {
// ๋ฉ์์ง๋ฅผ ์ถ๊ฐํ div ์์ ์ ํ
var conversationDiv = $("#conversation");
// ๋ฉ์์ง๋ฅผ ๋ด์ ์๋ก์ด div ์์ฑ
var messageDiv = $("<div>").addClass("chat-message").text(message);
// ํ์ฌ ์ ์ ์ธ์ง ํ์ธ
const isCurrentUser = message.includes(`[${getUsernameOrUnknown()}]`);
// ์ค๋ฅธ์ชฝ ์ ๋ ฌ์ด๋ฉด ํด๋์ค ์ถ๊ฐ
if (isCurrentUser) {
messageDiv.addClass("current-user");
}
// conversationDiv์ messageDiv ์ถ๊ฐ
conversationDiv.append(messageDiv);
// ์คํฌ๋กค์ ์ตํ๋จ์ผ๋ก ์ด๋ (์ ๋ฉ์์ง๊ฐ ๋ณด์ด๋๋ก)
conversationDiv.scrollTop(conversationDiv.prop("scrollHeight"));
}
$(function () {
$("#chatConnect").click(() => connect());
$("#chatDisconnect").click(() => disconnect());
$("#messageSend").click(() => sendMessage());
// textarea์์ ์ํฐ ํค ๊ฐ์ง
$("#chattingMessage").keydown(function (e) {
if (e.which === 13 && !e.shiftKey) {
e.preventDefault(); // ์ํฐ ํค ๊ธฐ๋ณธ ๋์ ๋ฐฉ์ง
$("#messageSend").click(); // ์ ์ก ๋ฒํผ ํด๋ฆญ
// ํ
์คํธ ์
๋ ฅ ๋ถ๋ถ์ผ๋ก ์ปค์ ์ด๋ ๋ฐ ๋ด์ฉ ๋น์ฐ๊ธฐ
$(this).val('').focus();
}
});
});
[Spring][iLanD] [Web Socket] ์ค์๊ฐ ์ฑํ
๊ธฐ๋ฅ ๋ค๋ฌ๊ธฐ
์น ์์ผ์ ํ์ฉํ ์ฑํ
๊ธฐ๋ฅ ๊ตฌํ ๊ณผ์
์น์์ผ์ ํ์ฉํ ์ฑํ
๊ธฐ๋ฅ์ ๊ตฌํํ๊ณ ์ํ๋ค.
Getting Started | Using WebSocket to build an interactive web application
์์กด์ฑ ์ถ๊ฐbuild.gradle ์ ์์กด์ฑ ์ถ๊ฐ
// websocket
implementation 'org.springframework.boot:spring-boot-starter-websocket'
Configuration ์ถ๊ฐ
@Configuration // ์คํ๋ง ํ๋ ์ ์ํฌ ์ค์ ํด๋์ค๋ก ๋ฑ๋ก
@EnableWebSocketMessageBroker // ์น์์ผ ์๋ฒ ํ์ฑํํ๋๋ฐ ์ฌ์ฉํ๋ค.
public class WebSocketConfig implements WebSocketMessageBrokerConfigurer {
// => ์น์์ผ ์ค์ ์ ์ฌ์ฉ์ ์ ์ํ๋ค.
@Override
public void configureMessageBroker(MessageBrokerRegistry config) {
config.enableSimpleBroker("/chatMessage");
//chatMessage ๋ก ์์ํ๋ ๋ชฉ์ ์ง ๋ฉ์์ง ๊ด๋ฆฌ
config.setApplicationDestinationPrefixes("/app");
//๋ชฉ์ ์ง๊ฐ /app ์์ ๋ฉ์์ง ๋ผ์ฐํ
}
@Override
public void registerStompEndpoints(StompEndpointRegistry registry) {
registry.addEndpoint("/websocket");
// URL์ ์น์์ผ ์๋ํฌ์ธํธ๋ก ๋ฑ๋กํ๋ค. ํด๋ผ์ด์ธํธ๋ ์ด URL์ ํตํด ์น์์ผ ์ฐ๊ฒฐ์ ์ด๊ธฐํ
}
}
๋ทฐ ์ปจํธ๋กค๋ฌ ์ถ๊ฐ
[Spring][iLanD] [Web Socket] ์ค์๊ฐ ์ฑํ
๊ธฐ๋ฅ ๊ตฌํ

[Spring][iLanD] [File] TroubleShooting - ํ์ผ ์ธ์ฝ๋ฉํด์ ์กฐํํ๊ธฐ

[Spring][iLanD] MVP - [AboutUs] @media ํ๊ทธ๋ฅผ ์ด์ฉํ ๋ฐ์ํ ์ค๋ฒ๋ ์ด ํ
์คํธ๋ฐ์ค

[Spring][iLanD] MVP - [File] ํ์ผ ์
๋ก๋ ํ๋ก์ ํธ ๊ฒฝ๋ก์ด๋ ๋ฐ ๋ฏธ๋ฆฌ๋ณด๊ธฐ ๊ตฌํ

[Spring][iLanD] MVP - [File] ํ์ผ ์
๋ก๋ ์
๋ฐ์ดํธ API ๊ตฌํ

[Spring][iLanD] [ํ๋ก์ ํธ์๋ฃ] ์ธ๋ถ ์ ์ ํ๊ฒฝ ๊ตฌ์ฑ ์๋ฃ ๋ชจ์ต

[Spring][iLanD] MVP - [File] ๊ณต์ง ์ฐ๊ด๊ด๊ณ ์ญ์ ์ ํ์ผ ์ญ์ ๊ตฌํ

[Spring][iLanD] MVP - [File] ํ์ผ ์
๋ก๋ API ๊ตฌํ

[Spring][iLanD] ์ธํธ๋ผ๋ท ์คํ ํ๊ฒฝ ๊ฐ๋จ ํ
์คํธ

[Spring][iLanD] MVP - [Notice] ํ์ด์ง ์ฒ๋ฆฌ ๊ตฌํ

[Spring][iLanD] MVP - [Admin] ๋ก๊ทธ์ธ ํ๋ก ํธ ์ฐ๊ฒฐ ๋ฐ ๊ด๋ฆฌ์ ํ์ด์ง ๊ตฌํ
์น ์์ผ์ ํ์ฉํ ์ค์๊ฐ ๊ทธ๋ฆผํ ๊ธฐ๋ฅ
[๋ช
์ฐ๋๋ท์ปด] ์น ์ค์๊ฐ ๊ทธ๋ฆผํ ๋ง๋ค๊ธฐ(4)
๋ชจ๋ฐ์ผ ํฐ์น ๋ฐ์์ด ์์ด์ ์ ์ธ๋จ
JS๋ก ๊ฒ์์ ๋ฃ๋๊ฒ ์ฌ์ดํธ, ๊ฒ์ ์์ฒด๋ง์ผ๋ก ํ๋์ ํ๋ก์ ํธ๊ธ์ด๋ผ ํด๋น ํ๋ก์ ํธ์์๋ ๊ตฌํ ์ด๋ ค์
์น ์์ผ์ ํ์ฉํ ์ฑํ
์๋ฒ ๊ธฐ๋ฅ
[Spring] ์น์์ผ์ผ๋ก ์ค์๊ฐ ์ฑํ
๊ตฌํ
WebSocket์ ์ด์ฉํ ์ค์๊ฐ ์ฑํ
๊ตฌํํ๊ธฐ 1: ์๋ฒ
[Spring][iLanD] ์ฌํ ๊ธฐ๋ฅ ๋ ํผ๋ฐ์ค ํ์

[Spring][iLanD] MVP - [Home] @media ํ๊ทธ๋ฅผ ์ด์ฉํ ๋ฉ์ธ ํ์ด์ง ๋ฐ์ํ ๋ฉ๋ด ๋ณ๊ฒฝ ์ ์ฉ
์ฐธ๊ณ ์๋ฃ
์คํ๋ง ๋ถํธ ํ์ผ ์ ๋ก๋ / ํ์ผ ๋ค์ด๋ก๋
โข
MultipartFileย ํด๋์ค ์ฌ์ฉ
โข
์๋์ผ๋ก MultipartConfigElement ํด๋์ค๋ฅผ ๋น์ผ๋ก ๋ฑ๋ก
โข
ํ์ผ๋ช
์ค๋ณต๋์ง ์๋๋ก UUID ์ฌ์ฉ
์คํ๋ง ๋ถํธ ํ๋ก์ ํธ์์ ํ์ผ ์ ๋ก๋ / ๋ค์ด๋ก๋ ์์
ํ์ผ ์ ๋ก๋
โข
ํ์ผ๋ช
์ค๋ณต๋์ง ์๋๋ก ํ์ผ ์ด๋ฆ ๋ณ๊ฒฝํด์ ์
๋ก๋
[Spring][iLanD] MVP - [File] ํ์ผ ์
๋ก๋ API ๋ ํผ๋ฐ์ค ํ์

[Spring][iLanD] MVP ํ๋ก ํธ์๋์ API ์ฐ๊ฒฐ 1์ฐจ ๊ธฐ๋ณธ CRUD, Ajax๋ก ๋์ ๋ค๋น๊ฒ์ด์
๊ตฌํ

[Spring][iLanD] MVP - [Video] ์์ CRUD API

[Spring][iLanD] MVP - [Info] ์ ๋ณด API

[Spring][iLanD] MVP - [Boards] ๊ฒ์ํ CRUD API

[Spring][iLanD] MVP - [Notice] ๊ณต์ง CRUD API
๊ฒ์ํ - ๊ฒ์๊ธ(4์ข
) - ํ์ผ
๋ชจ๋ ์ฝํ
์ธ ๋ ์ฌ์ค ๊ฒ์๊ธ์ด๋ผ ๋ณผ ์ ์๋ค. ๊ทธ ์ปฌ๋ผ์ ๋ด์ฉ์ด ๋ฌ๋ผ์ง๊ฑฐ๋ ์์น๋ง ๋ฌ๋ผ์ง ๋ฟ, ํ์ผ์ด ์๋๊ฒ ์๋๊ฒ ์ถํ์ ์ถ๊ฐ๊ฐ ํ์ํ ๊ฒ์ ๊ณ ๋ คํ์ฌ ํ์ผ ํ
์ด๋ธ์ ๋ณ๋๋ก ๋ถ๋ฆฌํ๊ณ ์ฐ๊ด๊ด๊ณ๋ก ์ฒ๋ฆฌํ๋ค.
Board.java
๊ฒ์ํ์ ํ์ด๋๋ ๊ฒ์ํ ์ํฐํฐ์ด๋ค. ๊ฐ ๊ฒ์๊ธ๋ค์ ์ข
๋ฅ๋ฅผ @OneToMany ์ฐ๊ด๊ด๊ณ๋ฅผ ํตํด์ ๊ฐ ๋ฆฌ์คํธ๋ค์ ๊ฐ์ ธ์ค๋๋ก ์ค์ ํ๋ค.
package com.yzpocket.iland.entity;
import jakarta.persistence.*;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;
import java.util.ArrayList;
import java.util.List;
@Entity
@Builder
@AllArgsConstructor
@NoArgsConstructor
@Getter
@Table(name = "boards")
public class Board extends TimeStamped{
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column
private String title;
@Column
@Enumerated(value = EnumType.STRING)
private BoardTypeEnum type;
@ManyToOne
@JoinColumn(name = "user_id")
private User user;
@OneToMany(mappedBy = "board")
List<Notice> noticeList = new ArrayList<>();
@OneToMany(mappedBy = "board")
List<Video> videoList = new ArrayList<>();
@OneToMany(mappedBy = "board")
List<Info> infoList = new ArrayList<>();
@OneToMany(mappedBy = "board")
List<Info> gameList = new ArrayList<>();
}
Notice.java
์ฒซ๋ฒ์งธ ๊ฒ์๊ธ์ ์ํฐํฐ์ธ ๊ณต์ง ๊ฒ์๊ธ์ด๋ค. ๊ฒ์ํ:๊ณต์ง๊ฒ์๊ธ ์ 1:N ์ฐ๊ด๊ด๊ณ๋ฅผ ๊ฐ์ง๊ณ ์๋ค. ์ด๊ฒ๊ณผ ๋์ผํ๊ฒ Info, Video, Game ๊ฒ์๊ธ์ด ๋์ผํ ์ปฌ๋ผ, ์ฐ๊ด๊ด๊ณ๋ฅผ ๊ฐ์ง๊ณ ์๋ค๊ณ ์๊ฐํ๋ฉด ๋๋ค. ๋ํ์ ์ผ๋ก Notice๋ถ๋ถ์ ์ฝ๋๋ ๋ค์๊ณผ ๊ฐ๋ค.
package com.yzpocket.iland.entity;
import jakarta.persistence.*;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;
import java.util.ArrayList;
import java.util.List;
@Entity
@Builder
@AllArgsConstructor
@NoArgsConstructor
@Getter
@Table(name = "notices")
public class Notice extends TimeStamped {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long noticeId;
@Column
private String noticeTitle;
@Column
private String noticeWriter;
@Column
private String noticeContents;
@Column
@Enumerated(value = EnumType.STRING)
private NoticeTypeEnum noticeType;
@ManyToOne
@JoinColumn(name = "board_id")
private Board board;
@OneToMany(mappedBy = "notice")
List<File> fileList = new ArrayList<>();
}
[Spring][iLanD] MVP - ์ฃผ์ ์ํฐํฐ ์์ฑ ๋ฐ ์ฐ๊ด๊ด๊ณ ๊ตฌ์ฑ
์ ์ฒด ๊ณผ์
1.
์์กด์ฑ ์ถ๊ฐ
2.
UserDetailsImpl ์์ฑ
3.
UserRepository ์ถ๊ฐ
4.
UserDetailsService ์์ฑ
5.
JWT์ ํธ ํด๋์ค ์ถ๊ฐ
6.
JWTํํฐ ์ถ๊ฐ
7.
์คํ๋ง ์ํ๋ฆฌํฐ ์ค์
[Spring][iLanD] MVP - [Auth] SpringSecurity JWT+์ฟ ํค ๋ฐฉ์ ๋ก๊ทธ์ธ ๊ธฐ๋ฅ ๊ตฌํ
ํ์ ๊ฐ์
, ์์ , ํํด API ๊ตฌํ
โข
๋น๋ฐ๋ฒํธ๋ SpringSecurity์ BCryptPasswordEncoder๋ฅผ ํตํด ๊ธฐ๋ณธ ๋จ๋ฐฉํฅ ํด์ ์๊ณ ๋ฆฌ์ฆ BCrypt ๋ก ๋ณตํธํ ๋ถ๊ฐ๋ฅํ ์ํธํ ๊ธฐ๋ฅ์ ํ์ฉ
โข
๊ธฐ๋ณธ์ ์ผ๋ก ์
๋ ฅ๊ฐ์ ์ ํจ์ฑ ๊ฒ์ฌ๋ ๋ค์๊ณผ ๊ฐ์
โข
usertype์ ํ๋ก ํธ์์ ๋๋กญ๋ค์ด๋ฐ์ค๋ก value๋ฅผ 0,1,2 ๋ฅผ ์ ๋ฌํ๋๋ก ์ค๊ณํ ์์
UserSignupRequestDto.java
@NoArgsConstructor
@AllArgsConstructor
@Getter
@Setter
@Builder
public class UserSignupRequestDto {
@Email
@NotBlank
private String useremail;
//@Pattern(regexp = "(?=.*[0-9])(?=.*[a-zA-Z]).{4,10}", message = "์์ด๋ 4~10์ ์๋ฌธ ์๋ฌธ์, ์ซ์๋ฅผ ์ฌ์ฉํ์ธ์.")
@Pattern(regexp = "^[๊ฐ-ํฃ]{2,4}$", message = "์ด๋ฆ์ ํ๊ธ๋ก 2๊ธ์์์ 4๊ธ์๊น์ง ์
๋ ฅํ์ธ์.")
private String username;
@Pattern(regexp = "^(?=.*[a-zA-Z])((?=.*\\d)|(?=.*\\W)).{8,15}+$", message = "๋น๋ฐ๋ฒํธ๋ 8~15์ ์๋ฌธ ๋ ์๋ฌธ์, ์ซ์๋ฅผ ์ฌ์ฉํ์ธ์.")
private String password1;
private String password2;
@Builder.Default
private long usertype = 0;
@Builder.Default
private String token = "";
}
UserUpdateRequestDto.java
@Data
@Builder
public class UserUpdateRequestDto {
@Pattern(regexp = "^(?=.*[a-zA-Z])((?=.*\\d)|(?=.*\\W)).{8,15}+$", message = "๋น๋ฐ๋ฒํธ๋ 8~15์ ์๋ฌธ ๋ ์๋ฌธ์, ์ซ์๋ฅผ ์ฌ์ฉํ์ธ์.")
private String password1;
private String password2;
}
[Spring][iLanD] MVP - [Users] ํ์ CUD API
โ ย ํ์ ๊ฐ์ ๋ฐ ๋ก๊ทธ์ธ ๊ตฌํ - staff only (๋นํ์์ ๋ชจ๋ ์ ์ )
โข
ํ์ ๊ฐ์
์์
โข
๋ก๊ทธ์ธ
โ ย Notice ๊ณต์ง๊ฒ์ํ
โข
CREATE - admin or staff only (์ค์๊ณต์ง์ปฌ๋ผํ์)
โข
READ
โข
UPDATE - admin or staff only
โข
DELETE - admin or staff only
[Spring][iLanD] ๊ธฐ๋ฅ ๋ถ๋ฅ

[Spring][iLanD] ๊ธฐ์ด ํ๋ก์ ํธ ๊ตฌ์กฐ ์์ฑ ๋ฐ ์์กด์ฑ ์ฃผ์
, ํ
์คํธ

[Spring][iLanD] ๋ฐ์ดํฐ๋ฒ ์ด์ค ์ค๊ณ 1์ฐจ(ERD)
ํ๋ก ํธ์๋ ์ปจ์
๋์์ธ
์ฃผ์ ์ปจํ
์ธ ๋์์ธ
[Spring][iLanD] ํ๋ก์ ํธ ๋๋ฉ ๋ฏธํ
, ํ๋ก ํธ์๋ ๋์์ธ ๊ตฌํ ์ํ, ์ฝ๋ ํ์ธ, ๊ณผ์
๋ฒ์ ํ์ธ
ํ๋ก์ ํธ ํ๊ธฐ
๋ฐฑ์๋ ์ฑ๋ฆฐ์ง์ ๋ํ ํ๊ณ
๋ฐฑ์๋ ์
์ฅ์์ ํ๋ก ํธ์๋์ ๋ํ ํ๊ณ
๊ธฐํ ๋๋์
[Spring][258] ํ๋ก์ ํธ ์๋ฃ ํ๊ธฐ ํ๊ณ , ๋ฐฐ์ด์ ๊ณผ ์์ฌ์ด์

[Spring][iLanD] ํ๋ก์ ํธ ์งํ ๋ก๋๋งต ์์ฑ

[Spring][iLanD] Git, Github ์ ์ฅ์ ์ฐ๊ฒฐ ๊ด๋ฆฌ
ํ๋ก์ ํธ ์ถ์ง ์ด์
์ฃผ๋ณ ๋คํธ์ํฌ๋ฅผ ํตํด ์์ง๋ง ์ค์ ๊ณผ ๊ฐ์ ๊ฒฝํ ๋์์ ์ฃผ๊ฒ ๋ค๋ ์ ์๊ฐ ๋ค์ด์์ผ๋ฉฐ ๊ฐ์ฌํ ๋ง์์ ๋คํด ์ต์ ์ ๋คํด ๋ฐฐ์ด ๊ฒ๋ค์ ํ ๋๋ก ํ๋ก์ ํธ๋ฅผ ์งํํด๋ณด๊ณ ์ ํฉ๋๋ค.
๊ต์ก๊ณผ์ ์์์ ์ฌ๋ฌ ํ ํ๋ก์ ํธ์ ๋ฌ๋ฆฌ ๋ณธ์ธ ์ค์ค๋ก ์ด๋์ ๋ ๊ฐ๋ฐ ํ ์ ์์์ง, ์ด๋ ๋ถ๋ถ๋ค์ด ๋ถ์กฑํ์ง ์ค์ ์ผ๋ก ๊ฒฝํํ๊ณ ์ถ์์ผ๋ฉฐ ํ ํ๋ก์ ํธ ํน์ฑ์ ๋ถ์
๊ณผ์ ์์ ์ง์ ๋ชป ๋ค๋ฃจ์ด๋ณธ ์ฌ๋ฌ ๊ธฐ๋ฅ๋ค์ ์ค์ค๋ก ๊ตฌํํด ๋ณผ ์ ์๋ ์ข์ ๊ธฐํ๋ผ๊ณ ์๊ฐํ์ต๋๋ค.
๋ณธ์ธ์ ๊ฐ๋ฐ ๋ฒ์, ํํฉ ํ์
โข
๊ฐ๋จํ Figma๋ฅผ ํตํด ์ ์๋ ์ปจ์
๋์์ธ์ ์์ผ๋ ๋ฐฑ์๋ API ๊ตฌ์ฑ์ ์ ํ ์๋ ์ํ
โข
API ๊ฐ๋ฐ์ด ํ์ํ๋ฉฐ ์ด์ ๋ฐ๋ผ ํ๋ก ํธ์๋์ ์ฝ๋ ๋๋ ๊ตฌ์ฑ์ด ๋ณ๊ฒฝ๋ ๊ฐ๋ฅ์ฑ์ด ์์
โข
๋์์ธ๊ณผ ์ปจ์
, ์๊ตฌ ๊ธฐ๋ฅ์ ์ต๋ํ ์ ์ง
โข
๋ฐ์ดํฐ๋ฒ ์ด์ค ์ค๊ณ ํ์
โข
Java ๊ธฐ๋ฐ์ SpringBoot ํ๋ ์์ํฌ๋ฅผ ์ฌ์ฉํ๋ ๊ฒ์ด ๊ฐ๋ฐ์์ ๊ฒฝํ์ , ๊ธฐ๊ฐ์ , ๊ฐํธ์ฑ์ ์ ํฉ
[Spring][iLanD] ๊ฐ๋ฐ ๊ณ์ฝ ์ฒด๊ฒฐ ๋ฐ ์ถ์ง ์ด์ , ๋ฒ์
Elasticsearch๋ ๋ํ ๋ด๋ถ์ ์ผ๋ก ์ ์์ ์ ์ด๋ฉฐ, ์ด๋ค ์๊ณ ๋ฆฌ์ฆ, ์๋ฃ๊ตฌ์กฐ ๋ฑ๋ฑ์ด๊ธฐ ๋๋ฌธ์ ๋น ๋ฅธ๊ฒ์ด ๋ณด์ฅ๋๋์ง์ ๋ํ ์ดํด๋ ์ถ๊ฐ ํ์
Elasticsearch๊ฐ ๋ด๋ถ์ ์ผ๋ก ์์ ์ ์ด๋ฉฐ ๋น ๋ฅธ ์ด์ ๋ ์ฌ๋ฌ ๊ฐ์ง๊ฐ ์์ต๋๋ค.
1.
๋จผ์ , Elasticsearch๋ Apache Lucene์ ๊ธฐ๋ฐ์ผ๋ก ๋ง๋ค์ด์ก์ต๋๋ค. Lucene์ ๊ฒ์ ์์ง์ ์ฌ์ฉ๋๋ ๊ฐ๋ ฅํ ์คํ ์์ค ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ก, ํ
์คํธ ์ธ๋ฑ์ฑ๊ณผ ๊ฒ์์ ํนํ๋ผ ์์ต๋๋ค.
2.
Elasticsearch๋ ๋๋์ ๋ฐ์ดํฐ๋ฅผ ํจ์จ์ ์ผ๋ก ์ฒ๋ฆฌํ๊ธฐ ์ํด ์ฌ๋ฌ ๊ฐ์ง ์๊ณ ๋ฆฌ์ฆ๊ณผ ์๋ฃ๊ตฌ์กฐ๋ฅผ ์ฌ์ฉํฉ๋๋ค. ๊ทธ ์ค ํ๋๋ ์ญ์์ธํ๋ผ๋ ๊ฐ๋
์
๋๋ค. ์ญ์์ธํ๋๋ ๋จ์ด๊ฐ ์ด๋ ๋ฌธ์์ ๋ํ๋๋์ง๋ฅผ ๊ธฐ๋กํด ๋์ด ๊ฒ์ ์๋๋ฅผ ํฅ์์ํค๋ ๋ฐ ๋์์ด ๋ฉ๋๋ค.
3.
Elasticsearch๋ ๋ถ์ฐ ์์คํ
์ผ๋ก ์ค๊ณ๋ผ ์์ด์ ์ฌ๋ฌ ๋
ธ๋์ ๋ฐ์ดํฐ๋ฅผ ๋ถ์ฐ ์ ์ฅํ๊ณ ๋ณ๋ ฌ ์ฒ๋ฆฌ๋ฅผ ๊ฐ๋ฅํ๊ฒ ํฉ๋๋ค. ์ด๋ ๊ฒ ํจ์ผ๋ก์จ ๋ฐ์ดํฐ ๊ฒ์ ๋ฐ ๋ถ์ ์์
์ ํจ์จ์ ์ผ๋ก ์ฒ๋ฆฌํ ์ ์์ต๋๋ค.
4.
Elasticsearch๋ ์ค์๊ฐ ๊ฒ์์ ์ง์ํ๊ณ ์์ด์ ๋ฐ์ดํฐ๊ฐ ์
๋ฐ์ดํธ๋๋๋ผ๋ ๊ฑฐ์ ์ค์๊ฐ์ผ๋ก ๊ฒ์ ๊ฒฐ๊ณผ๋ฅผ ๋ฐ์ํ ์ ์์ต๋๋ค. ์ด๋ ๋๊ท๋ชจ์ ๋ฐ์ดํฐ์
์์๋ ๋์ ์ฑ๋ฅ์ ์ ๊ณตํ๋ ๋ฐ ๊ธฐ์ฌํ๊ณ ์์ต๋๋ค.
์๋ผ์คํฑ ์์น์ ๊ฒ์์ด ๋น ๋ฅธ ์ด์ ?
[Spring][258] Elasticsearch์ Lucene ์ธ๋ฑ์ฑ
์ง๋ฌธ์ด ๋์ฌ๋งํ ๋ถ๋ถ์ ๋ณธ๊ฒฉ์ ์ผ๋ก ์ํคํ
์ฒ ๊ธฐ์ ๋์
๋ฐฐ๊ฒฝ๋ถํฐ ์์๋๋ค.
์ ์ฒด์ ์ธ ์์ฒญ์ ํ๋ฆ?
์ ์ฒด์ ์ผ๋ก ํด๋ผ์ด์ธํธ์ HTTP ์์ฒญ์ด ์ด๋ป๊ฒ ์ฒ๋ฆฌ๋๋์ง ์๋ ค์ฃผ์ธ์.
โข
๊ธฐ๋ณธ ํ๋ฆ
๊ทธ ํ๋ฆ์์ ์นดํ์นด๋ ์ด๋ค ์ญํ ์ ํ๋์?
โข
๊ธฐ๋ณธ์ ์ผ๋ก ์ ํฌ๋ ๋ฉ์ธ์๋ฒ์, ์ด๋ฒคํธ๋ง์ ์ํ ๋
๋ฆฝ์ ์ธ ์๋ฒ๊ฐ ๋ณ๋๋ก ์กด์ฌํฉ๋๋ค.
โข
์๋ฒ๊ฐ ๋๋์ด์ก๊ณ ๊ทธ ์ค๊ฐ ์ญํ ๋ก ์นดํ์นด๊ฐ ๋ฐ์ดํฐ๋ฅผ ์ ๋ฌํ๋ ์ญํ ์
๋๋ค.(๋ฉ์์งํ)
[Spring][258]
ย ์์ ์ง์ ์๋ต ๋ชฉ๋ก - ํ๋ก์ ํธ์ ๋ํ์ฌ ๋ฐํํ๋ฆ์ ๋ฐ๋ฅธ ๋ถ๋ฅ
ย ์์ ์ง์ ์๋ต ๋ชฉ๋ก - ํ๋ก์ ํธ์ ๋ํ์ฌ ๋ฐํํ๋ฆ์ ๋ฐ๋ฅธ ๋ถ๋ฅJPQL ์ฟผ๋ฆฌ๋ก ์ง์ ๊ฒ์ ๋ฐฉ์ ์ ํ๊ธฐ
@Repository
public interface ElasticBookSearchRepository extends ElasticsearchRepository<ElasticsearchBook, Long>, ElasticCustomBookSearchRepository {
// ์ด๊ฑด ๊ฐ๋ฐ์๊ฐ ์ง์ ์ฐ์ฐ๋ถ๋ฅผ ์์ฑํ๋ ๋ฐฉ์์
// ์ ์ด๊ถ์ด ๊ฐ๋ฐ์์ ์์
//List<ElasticsearchBook> findByBookNameContains(String keyword);
// Elasticsearch API์ ์ฟผ๋ฆฌ๋ฌธ์ ์์ฑํด์ ๋ณ๊ฒฝํด๋ณด๋ ๋ฐฉ๋ฒ
// ์ด๊ฑด ์ ์ด๊ถ์ด Spring Data Elasticsearch ์ ์์
// ์ฟผ๋ฆฌ๋ฅผ SPE๊ฐ ๋ถ์ํจ
//QueryStringQuery - ์๋ผ์คํฑ์์น ๋ด์ฅ ๊ธฐ๋ณธ ์ฟผ๋ฆฌ
@Query("{\"query_string\": {\"query\": \"?0\"}}")
List<ElasticsearchBook> findByQueryStringQuery(String queryString, Pageable pageable);
//์กฐ๊ฑด๊ฒ์(bool)
//@Query("{\"bool\": {\"must\": {\"query_string\": {\"query\": \"?0\"}}}}")
//List<ElasticsearchBook> findByQueryStringQuery(String queryString, Pageable pageable);
/*
must: [ํ๋] AND [์ปฌ๋ผ] = [์กฐ๊ฑด]
must_not: [ํ๋] AND [์ปฌ๋ผ] != [์กฐ๊ฑด]
should: [ํ๋] OR [์ปฌ๋ผ] = [์กฐ๊ฑด]
filter: [ํ๋] [์ปฌ๋ผ] IN ( [์กฐ๊ฑด] )
*/
}
์๋ ์ฟผ๋ฆฌ๋ฅผ @Query ์ด๋
ธํ
์ด์
์ ํตํด์ ์์ฑํ์ฌ ๊ฒ์์ ๋ฐฉ์์ ๋ณ๊ฒฝ ํ ์ ์๋ค. ๊ธฐ์กด ์ง์ ๋ด๋ถ ๋ก์ง์ ๊ตฌ์ฑํ๋ ๋ฐฉ์๊ณผ ๋ค๋ฅด๊ฒ, ๋ง์น JPQL์ ์ฌ์ฉํ๋๊ฒ ์ฒ๋ผ ์ฟผ๋ฆฌ๋ฌธ์ ์์ฑ ํ ์ ์๋ค. ๊ทธ๋ผ ๋ง์ฐฌ๊ฐ์ง๋ก ์ฟผ๋ฆฌ๋ฌธ์ด ์ปดํ์ผ ๋จ๊ณ์์ ๊ฒ์ฌ๋์ง ์๋ ๋จ์ ์ด ์์ ๊ฒ์ผ๋ก ๋ณด์ฌ์ง๋ค.
QueryDSL๋ก ๊ตฌํํด๋ณด๊ธฐ
build.gradle ์์กด์ฑ ์ถ๊ฐ
dependencies {
implementation 'com.querydsl:querydsl-apt:4.x.x'
implementation 'com.querydsl:querydsl-elasticsearch:4.x.x'
}
QueryDSL ํ๋ฌ๊ทธ์ธ ๋ฐ ํ์คํฌ ์ค์
plugins {
id "com.ewerk.gradle.plugins.querydsl" version "1.0.10"
}
querydsl {
jpa = false
querydslSourcesDir = 'src/main/generated'
}
sourceSets {
main {
java {
srcDir 'src/main/generated'
}
}
}
configurations {
querydsl.extendsFrom compileClasspath
}
compileQuerydsl {
options.annotationProcessorPath = configurations.querydsl
}
[Spring][258] Elasticsearch APIs
[2:22] ELB ์ค๋ช
ํ์ค ๋ https๋ฅผ ์ธ๊ธํ์
จ๋๋ฐ, ๋ฐํ์๋ฃ์๋ ๊ทธ ๋ด์ฉ์ด ๋ฐ์๋์ด ์์ผ๋ฉด ์ข์ ๊ฒ ๊ฐ์ต๋๋ค. ๊ทธ๋ฆฌ๊ณ main server์ event server์ spring architecture๋ฅผ ๋ชจ๋ ๋ณด์ฌ์ค ํ์๋ ์๊ณ , application server๋ก ์ ์ํ ๋ค์ ํด๋น server์ ์คํ์ ํ๋๋ง ๋ณด์ฌ์ฃผ๊ณ main server์ event server๊ฐ application server ๊ตฌ์กฐ๋ก ๋์ด์๋ค๊ณ ๋ณด์ฌ์ฃผ๋ฉด ๋ ๊น๋ํ ๊ฒ ๊ฐ์ต๋๋ค.
๋ฐํ์๋ฃ ์์ โ
๋๋ณธ ์์ โ
์์ ๋ฉํธ ์์ โ
[2:58] mysql DB๋ก ๋ถํฐ ๋์ ๋ฐ์ดํฐ๊ฐ ๋๊ธฐํ๋๋ค๋ ํํ๋ณด๋ค๋, logstash๊ฐ ์ ์ฒ๋ฆฌํ ๋์ ๋ฐ์ดํฐ๊ฐ elasticsearch์ ์ ์ฅ๋๊ณ , ์ด ๋ฐ์ดํฐ๊ฐ mysql์ ๋๊ธฐํ๋๊ณ ์๋ค๋ผ๊ณ ํํํ๋๊ฒ ๋ง์ ๊ฒ ๊ฐ์ต๋๋ค.
๋ฐํ์๋ฃ ์์ ์์โ
๋๋ณธ ์์ โ
[Spring][258] ๋ฐํ ํผ๋๋ฐฑ ์์ [์๋ฃ]
Spring Boot
Spring Security
JWT
Swagger
Spring Data JPA
Querydsl
Elasticsearch
[Spring][258]
ย ์์ ์ง์ ์๋ต ๋ชฉ๋ก - ๊ธฐ์ ์คํ๊ณผ ๊ด๋ จ๋ ๊ฒ๋ค
ย ์์ ์ง์ ์๋ต ๋ชฉ๋ก - ๊ธฐ์ ์คํ๊ณผ ๊ด๋ จ๋ ๊ฒ๋ค
[Spring][258] ํ๋ก์ ํธ ๋ง๋ฌด๋ฆฌ ์์
์ธ์ฉ
๊ฐ์ฉ์์ต์ ํ
โข
์ง์์๋ต ์๊ฒฌ๊ฒฐ์ ๊ด๋ จ๋ด์ฉ ๋ชจ๋์ ๋ฆฌ
โข
API ๋ช
์ธ์ ํ ์ ๋ฆฌ, ์ค์จ๊ฑฐ๋ ๊ฒฐ๊ณผ์ธ ๊ฒ์ด๋ฏ๋ก ํ์๋ ๋ค๋ฅด๋ค. ์ด๊ฒ์ ๋ฏธ๋ฃจ์๋ ์ผ์ผ๋ฟ.
[Spring][258] [์ค์๊ณต์ง] ํ๋ก์ ํธ ๋ง๋ฌด๋ฆฌ๋จ๊ณ ํ์ฌ ํ์ํ ์์
์
๋ฌด๋ถ๋ด์
๋๋ค.[์๋ฃ]
Q. ๊ฒ์ ์ฑ๋ฅ์ด ์ ์ง์ ์ผ๋ก ๊ฐ์ ๋์ง ์๊ณ ํ๋ฒ์ ์ฌ๋์ต๋๋ค.
A. ์ ์ง์ ์ผ๋ก ๊ฐ์ ๋๊ฑฐ๋ ๊ทธ๋ฐ ํ
์คํธ ๊ฒฐ๊ณผ๋ฅผ ์ํ๊ฒ ์ง๋ง, ๊ทธ๋ ์ง ์์ ์๋ ์์ต๋๋ค. ์ค์ ๋ก ๊ทธ๋ ๊ธฐ๋ํฉ๋๋ค. ์คํ๋ ค ์ฑ๋ฅ์ด ๋์ ๋๋ ์์ต๋๋ค. ํ์ง๋ง ์ค์ํ๊ฑด ๊ทธ ํ
์คํธ ์๋ฃ๋ค์ ๊ทธ๋ ๋ค๊ณ ํด์ ํ์ ์๋๊ฒ์ด ์๋๋๋ค. ์คํ๋ ค ๊ทธ๋ฐ ๋ชจ์ต์ ์ฐ์ถํ๋ ค๊ณ ์กฐ์๋๋ฉด์๋๊ณ ๊ทธ๋๋ก ๋ํ๋๋๊ฒ์ด ๋ง์ต๋๋ค. ๊ทธ๋์ ์ง๊ธ ๊ฒฐ๊ณผ๊ฐ ๋ง๋๊ฒ์
๋๋ค.
Q. ํฅํ ํ๋ก์ ํธ์์ ์ด๋ค ๋ถ๋ถ์ ์ค์ ์ผ๋ก ์งํํ๋ฉด ์ข์ ๊น์?
A. FTI์ Elasticsearch๋ฅผ ๋์
ํ ๋ถ๋ถ์์ ํํ
์คํธ์ธ๋ฑ์ค์ ํธ์ฐจ๊ฐ ๋ฐ์ํ๋ ๋ถ๋ถ์ ๋ ๊น๊ฒ ํ๋ณด์์ผ๋ฉด ์ข์์๊ฒ ๊ฐ์ต๋๋ค. ๋ฌผ๋ก ํ๋ก์ ํธ ๋๋๊ณ ๋ ๊ณ์ํด์ ์ฐพ์๋ณผ ํ์๊ฐ ์์ต๋๋ค.
MySQL์ ํํ
์ธ๋ฑ์ค์ ๋ํด์ ์๊ณ ๋ฆฌ์ฆ์ด๋ ์๋ฃ๊ตฌ์กฐ๊น์ง ํ์ฅํด๋ณด๊ณ ๋ํด์ ๋ํดํธ ์์ง์ธ InnoDB์ ๋ํด์๊น์ง ๊ทธ ์ฐจ์ด๋ฅผ ๋ฐ์์ํค๋๊ฒ์ด ๋ฌด์์ธ์ง ์์๋ณด๊ธฐ๊น์ง..
๊ทธ ํธ์ฐจ์ ์์ธ, ๋ฌด์์ด ๊ทธ๋ ๊ฒ ๋ง๋ค์๋์ง ์ฐพ์๊ฐ๋ ๊ณผ์ ์ ๊ณ์ํด์ ๊ณต๋ถํด๋ณด์๋๊ฒ ๋์ ๋ ์ ์์ต๋๋ค. ๊ทธ๊ฒ ๊ทธ ๋ถ๋ถ์ ๋ํด์ ๋ค๋ฅธ ์ด๋ ๊ฐ๋ฐ์๋ณด๋ค ์ ์๋, ์ ์ด๋ฐ ํ์์ด ๋ํ๋๋์ง ์๋ ๊ฐ๋ฐ์๊ฐ ๋๋ ๊น์ ๊ณต๋ถ ๋ฐฉ๋ฒ์
๋๋ค.
ํนํ ์ด๋ฐ ๊น์ ๊ณต๋ถ๋ฅผ ํ ๋๋ ๊ณต์๋ฌธ์๋ฅผ ์ฐธ๊ณ ํ๋๊ฒ์ด ๊ฐ์ฅ ์ข์ต๋๋ค. ๊ฐ์ฅ ๋ช
ํํ ๊ทผ๊ฑฐ์ด๊ธฐ ๋๋ฌธ์
๋๋ค.
๋ฉํ ๋์ ์ถฉ๊ณ
๊ณตํต์ ์ผ๋ก ๊ธฐ์ ์คํ๋ค์ ๋ํด์ ๊ทผ๊ฑฐ๋ฅผ ์ ์ ๋ฆฌํด์ค๊ณ ์์ง๋ง ๋ํ ๊ณ์ํด์ ๋ณด๋ค ๋ ์ ๋ฆฌํ๋ ๊ฒ์ด ์ค์ํฉ๋๋ค. ๊ทธ ์ด์ ๋ ์ด๋ ๊ณง ๊ธฐ์ ์ ์ ํ ๋๋ ํ๋จํ๋ ๋ฅ๋ ฅ์ด๊ณ , ๊ทธ ๊ธฐ์ ์ ์ ํํ ๊ฒ์ด ๋ง๋ค๋ ๊ทผ๊ฑฐ๋ฅผ ๋ง๋๋ ๋ฅ๋ ฅ. ์ด๊ฒ ์ค๋ฌด์์ ์ค์ ๋ก ์ด๋ฃจ์ด์ง๊ธฐ ๋๋ฌธ์
๋๋ค.
ํ๋ก์ ํธ ๋์ ์์ฃผํ๋ฉด ์๋ฉ๋๋ค. ๊ฐ๋ฐ๊ณต๋ถ ์์์
๋๋ค.
๋ง๋ฌด๋ฆฌ ๋ ์ ์๋ ๋ถ๋ถ์ ๋ง๋ฌด๋ฆฌํด์ผ๊ฒ ์ง๋ง, ์์ผ๋ก ๋ํด์ผํ ๊ฒ์ด ๋ง๊ณ ,
์คํธ๋ ์ค ๊ด๋ฆฌ์ ๋ฒ์์์ ์กฐ์ ํด์ผ ํฉ๋๋ค.
[Spring][258] [5์ฃผ์ฐจ ๋ฉํ ๋ง] ์ง๋ฌธ๋ด์ฉ ์ ๋ฆฌ์ ๋ต๋ณ + ์กฐ์ธ

[Spring][258] ๊ฐ๋จ์๊ฐ ์์ ๊ตฌ์[์๋ฃ]
๋ฐํ์๋ฃ์ ๊ตฌ์ฑ
์ฒซํ๋ฉด
๋ชฉ์ฐจ
์ข
๋ฃํ๋ฉด
๋ฐํ์๋ฃ์ ์์์ ํ์ฉ
์์์ ํผํฐ ํ๋ฆ์ ์ ์ ํ ๋ด์ฉ๋ค์ ๊ฐ๊ฒฐํ๊ณ ์ถฉ๋ถํ๊ฒ ์ค๋ช
๋๋๋ก ๊ตฌ์ฑ
์ฝํฌ๋ ๊ธฐ๋ณธ
[Spring][258] ๋ฐํ ์์&PPT ๊ตฌ์ฑ[์๋ฃ]
ํ๋ก์ ํธ ๊ธฐํ ๋ฐฐ๊ฒฝ ์ค๋ช (30์ด)
โข
ํด๋น ํ๋ก์ ํธ๋ฅผ ์ด๋ ํ ๋ฐฐ๊ฒฝ๊ณผ ๋น์์ฑ์ผ๋ก ๊ธฐํํ๊ฒ ๋์๋์ง ์ค๋ช
ํด์ฃผ์ธ์.
โข
โํ์ค์์ ์ด๋ ํ ๋ฌธ์ ๋ฅผ ๋ฐ๊ฒฌํ์ฌ ์ด๋ค ๊ธฐ์ ์ ํ์ฉํ๋ฉด ์ด ๋ฌธ์ ๋ฅผ ํด๊ฒฐํ ์ ์์ ๊ฒ ๊ฐ์๋ค.โ
์์ฐ ์์ ์ฌ์ (1๋ถ)
โข
์๋น์ค์ ํต์ฌ์ด ๋๋ ๊ธฐ๋ฅ๋ง ์์ฐํด ์ฃผ์ธ์. ์ฆ, ํ์๊ฐ์
, ๋ก๊ทธ์ธ ๊ณผ์ ๋ฑ์ ์๋ตํด๋ ๋ฉ๋๋ค.
โข
์๋น์ค ์์ฐ์๋ ๋ณ์๊ฐ ๋ง์ต๋๋ค. ๊ฐ๋ฅํ ๋
นํ ์์์ ํ์ฉํ์ฌ ๋ฐํ๋ฅผ ์งํํด์ฃผ์ธ์.
[Spring][258] ๋ฐํ ๋๋ณธ ํ๋ฆ ๊ตฌ์[์๋ฃ]
์ด์
์ ํฌ ํ๋ก์ ํธ์ CI/CD์ ์ํํธ์จ์ด ์ํคํ
์ฒ๋ฅผ ์ค๋ช
๋๋ฆฌ๊ฒ ์ต๋๋ค.
์ ํฌ์กฐ๋ CI/CD๋ฅผ Github Action๊ณผ AWS์ S3, Codedeploy๋ฅผ ์ด์ฉํด์ ๊ตฌํํ์์ต๋๋ค.
์ ํฌ GitHub์์ main ๋ธ๋์น๋ก pull request๋ฅผ ๋ณด๋ด๋ฉด GitHub Action์ ์ด์ฉํด์ AWS์ S3์ Codedeploy๋ฅผ ํตํด AWS EC2 ์๋ฒ๋ก ๋ฐฐํฌ๋๋ ๊ตฌ์กฐ์
๋๋ค.
๊ทธ๋ผ ์ด์ ์ ํฌ๊ฐ ์ GIthub Action์ผ๋ก CI/CD๋ฅผ ๊ตฌ์ฑํ์๋์ง ๋ง์๋๋ฆฌ๊ฒ ์ต๋๋ค.
์ ํฌ๋ ์ด๊ธฐ Jenkins์ GitHub Action ์ฌ์ด์์ ๊ณ ๋ฏผ์ ํ์์ต๋๋ค.
Jenkins๋ ๋ณ๋์ ์๋ฒ๋ฅผ ์ค๋นํ๊ณ ์ ์ง๋ณด์ํด์ผ ํ๋ ๋น์ฉ๊ณผ ๋
ธ๋ ฅ์ด ํ์ํ๋ฉฐ, AWS CI/CD์ ํนํ๋ ๋๊ตฌ๋ค๊ณผ์ ํตํฉ์ ์ํด ์ถ๊ฐ์ ์ธ ํ๋ฌ๊ทธ์ธ์ด๋ ์ค์ ์ด ์๊ตฌ๋ฉ๋๋ค.
Jenkins๊ฐ ๊ฐ๋ ฅํ ์ปค์คํฐ๋ง์ด์ง์ ์ ๊ณตํ์ง๋ง, GitHub Actions๋ AWS์์ ํตํฉ ๋ฐ ๊ด๋ฆฌ ์ธก๋ฉด์์ ๋ ๊ฐํธํ๊ณ ํจ์จ์ ์ธ ์๋ฃจ์
์ ์ ๊ณตํฉ๋๋ค.
๋ํ GitHub Action์ ์ฌ์ฉํ๋ฉด GitHub ๋ฆฌํฌ์งํ ๋ฆฌ์์ ๊ธด๋ฐํ ํตํฉ์ผ๋ก ์ด๋ฒคํธ ๊ธฐ๋ฐ ์ํฌํ๋ก๋ฅผ ์ฝ๊ฒ ์ค์ ํ ์ ์๊ณ ์ฝ๋ ์์ค์ CI/CD ํ์ดํ๋ผ์ธ์ด ๋์ผํ ์ฅ์์์ ๊ด๋ฆฌ๋๋ฏ๋ก ๊ฐ๋ฐ์๋ ๋ณ๋์ CI ๋๊ตฌ๋ก ์ ํํ ํ์ ์์ด ํธ๋ฆฌํ๊ฒ ์์
ํ ์ ์๊ธฐ์ ์ ํํ์์ต๋๋ค.
๊ทธ๋ผ ์ด์ ์ ํฌ ์ํํธ์จ์ด ์ํคํ
์ฒ์ ๋ํ์ฌ ์ค๋ช
ํ๊ฒ ์ต๋๋ค.
ํด๋ผ์ด์ธํธ๊ฐ ์์ฒญ์ ํ ์ ์ฐ์ ELB ( ALB )์์ ํด๋ผ์ด์ธํธ๊ฐ https ํ๋กํ ์ฝ์ ์ฌ์ฉ ํ๋์ง ์ดํด๋ณด๊ณ http๋ฉด https ํต์ ์ ํ๋๋ก ์ํ์ฝ๋ 301์ ํตํด ๋ฆฌ๋๋ ์
์ ํฉ๋๋ค. ํด๋ผ์ด์ธํธ๊ฐ HTTPS ํ๋กํ ์ฝ์ ์ฌ์ฉํ๋ฉด Main Server๋ก ์ ๋ฌํด ์ค๋๋ค. ์ฌ๊ธฐ์ ํด๋ผ์ด์ธํธ๊ฐ ์ฌ๋ฟ์ด๋ฉด ์์ฒญ์ ๋๋์ด์ ํ ์๋ฒ์์ ๋ชจ๋ ์์ฒญ์ ์ฒ๋ฆฌํ์ง ์๊ฒ ๋ถ๋ฐฐํด์ค๋๋ค. ๊ทธ ํ Main ์๋ฒ์์๋ ํด๋ผ์ด์ธํธ๊ฐ ์์ฒญํ ์๋น์ค๊ฐ ๋ง์ดํฌ๋ก์๋น์ค ์ํคํ
์ฒ ๊ตฌ์กฐ๋ฅผ ์ ์ฉํ ์ฑ
๋๋ ์๋น์ค์ธ์ง ์ฌ๋ถ๋ฅผ ํ์
ํฉ๋๋ค. ์ฑ
๋๋ ์๋น์ค๋ ์งง์ ์๊ฐ ๋๊ท๋ชจ์ ํธ๋ํฝ์ด ์์๋๋ ์๋น์ค์ด๊ธฐ์ ๋ง์ดํฌ๋ก์๋น์ค ์ํคํ
์ฒ ๊ตฌ์กฐ๋ฅผ ์ ์ฉํ์์ต๋๋ค. ๋ง์ฝ ์ฑ
๋๋ ์๋น์ค๊ฐ ์๋๋ผ๋ฉด ๋ฉ์ธ์๋ฒ์์ ์์ฒด์ ์ผ๋ก ์ฒ๋ฆฌํฉ๋๋ค. ๊ทธ๋ฆฌ๊ณ ์ฑ
๋๋ ์๋น์ค์ธ ๊ฒฝ์ฐ์๋ ํ๋ก๋์๋ฅผ ํตํด ์นดํ์นด๋ก ํด๋ผ์ด์ธํธ์ ์์ฒญ ๋ฐ์ดํฐ๋ฅผ ๋ณด๋ด๊ณ ์ด๋ฒคํธ ์๋ฒ์์๋ ์ปจ์๋จธ๋ก ์์ฒญ ๋ฐ์ดํฐ๋ฅผ ๋ฐ์ ์ดํ ๋ ๋์ค์ ๋ถ์ฐ๋ฝ์ ํตํด ๋์์ฑ ์ ์ด๋ฅผ ํ๋ฉฐ ๋น์ฆ๋์ค ๋ก์ง์ ์ฒ๋ฆฌํ ๊ฒฐ๊ณผ๋ฅผ ๋ค์ ํ๋ก๋์๋ฅผ ํตํด ์นดํ์นด ์๋ฒ๋ก ๋ณด๋
๋๋ค. ๋ฉ์ธ์๋น์ค์์๋ ์ปจ์๋จธ๋ฅผ ํตํด ๋ณด๋ธ ์์ฒญ์ ํด๋นํ๋ ์ด๋ฒคํธ ์๋ฒ์ ๊ฒฐ๊ณผ๋ฅผ ๋น๋๊ธฐ์ ์ผ๋ก ์ฒ๋ฆฌํ์ฌ ํด๋ผ์ด์ธํธ์๊ฒ ์ ๋ฌํด์ค๋๋ค.
1์ฐจ ์์ ๋ฐ ์ ๋ฆฌ
ํ๋ก์ ํธ์ CI/CD ๋ฐ ์ํํธ์จ์ด ์ํคํ
์ฒ์ ๋ํด ์๋ดํด ๋๋ฆฌ๊ฒ ์ต๋๋ค.
์ ํฌ ํ์ Github Action, AWS์ S3, ๊ทธ๋ฆฌ๊ณ Codedeploy๋ฅผ ํ์ฉํ์ฌ CI/CD๋ฅผ ๊ตฌ์ฑํ์ต๋๋ค.
์ด๋ฅผ ํตํด, Github์ main ๋ธ๋์น๋ก pull request๊ฐ ์ด๋ฃจ์ด์ง๋ฉด ์๋์ผ๋ก AWS EC2 ์๋ฒ๋ก ์ฝ๋๊ฐ ๋ฐฐํฌ๋๋ ์์คํ
์ ๊ฐ์ถ์์ต๋๋ค.
CI/CD ์์คํ
์ ํ์ ์์ด, ์ ํฌ๋ Jenkins์ GitHub Action ์ค ๊ณ ๋ฏผ ๋์ ํ์๋ฅผ ์ ํํ์ต๋๋ค. Jenkins๋ ๊ฐ๋ ฅํ ๋ง์ถค ์ค์ ์ด ๊ฐ๋ฅํ์ง๋ง ๋ณ๋์ ์๋ฒ ์ ์ง ๋ฐ AWS์์ ํตํฉ์ ์ํ ์ถ๊ฐ ์์
์ด ํ์ํ ๋ฐ๋ฉด, GitHub Action์ AWS์์ ํตํฉ์ด ์ฉ์ดํ๊ณ ๊ด๋ฆฌ๊ฐ ๋ ๊ฐ๋จํ์ฌ ๊ฐ๋ฐ์๊ฐ ๋ ์ฝ๊ฒ ์์
ํ ์ ์๋ ์ฅ์ ์ด ์์ต๋๋ค.
๋ค์์ผ๋ก ์ ํฌ ์ํํธ์จ์ด ์ํคํ
์ฒ๋ฅผ ์ค๋ช
ํ๊ฒ ์ต๋๋ค.
ํด๋ผ์ด์ธํธ์ ์์ฒญ์ด ๋ค์ด์ค๋ฉด ELB(ALB)๊ฐ ์ด๋ฅผ ๋ฐ์์ HTTP ํ๋กํ ์ฝ์ ์ฌ์ฉํ๋์ง ํ์ธํ๊ณ , ํ์ํ ๊ฒฝ์ฐ HTTPS๋ก ๋ฆฌ๋๋ ์
ํฉ๋๋ค.
์์ ํ HTTPS ํ๋กํ ์ฝ์ ์ฌ์ฉํ ๊ฒฝ์ฐ ์์ฒญ์ ๋ฉ์ธ ์๋ฒ๋ก ์ ๋ฌ๋๋ฉฐ, ์ด ์๋ฒ๋ ์์ฒญ์ ์ ์ ํ ๋ถ์ฐํ์ฌ ์ฒ๋ฆฌํฉ๋๋ค.
ํนํ ๋๊ท๋ชจ ํธ๋ํฝ์ ์ฒ๋ฆฌํด์ผ ํ๋ ์ฑ
๋๋ ์๋น์ค ๊ฐ์ ๊ฒฝ์ฐ, ๋ง์ดํฌ๋ก์๋น์ค ์ํคํ
์ฒ๋ฅผ ์ ์ฉํ์ฌ ์์ฒญ์ ์ฒ๋ฆฌํฉ๋๋ค. ์ด๋ ์นดํ์นด๋ฅผ ํตํด ์ด๋ฒคํธ ๊ธฐ๋ฐ์ผ๋ก ๋ฐ์ดํฐ๋ฅผ ์ฒ๋ฆฌํ๊ณ , ๋ ๋์ค์ ๋ถ์ฐ ๋ฝ์ ํ์ฉํด ๋์์ฑ์ ์ ์ดํจ์ผ๋ก์จ ํจ์จ์ ์ธ ์๋น์ค๋ฅผ ์ ๊ณตํฉ๋๋ค.
๋ง์ง๋ง์ผ๋ก, ๋ฉ์ธ ์๋น์ค๋ ์ด๋ฒคํธ ์๋ฒ์ ๊ฒฐ๊ณผ๋ฅผ ๋น๋๊ธฐ์ ์ผ๋ก ๋ฐ์ ํด๋ผ์ด์ธํธ์๊ฒ ์ ๋ฌํฉ๋๋ค.
[Spring][258] CI/CD ๋ฐ ์ํํธ ์จ์ด ์ํคํ
์ฒ ๋์ ํ๋ฆ ์ ๋ฆฌ

[Spring][258] ํ๋ก ํธ ๋์์ธ ์์
_ ์ฑ
๋๋ ์ด๋ฒคํธ ํ์ด์ง
MySQL DB์ ๋ฐ์ดํฐ์ Elasticsearch ๋ฐ์ดํฐ๋ ๋๊ธฐํ๋ก ์ผ๊ด์ฑ์ ์ ์งํด์ผ ํ๋ค.
๊ธฐ์กด MVP ๊ธฐ๋ฅ์ผ๋ก ๋์์ ์ ๋ณด๋ฅผ ๋ณ๊ฒฝํ๋ ๋ถ๋ถ๋ค์ด ์์ต๋๋ค. ํนํ Elasticsearch์ ์ฐ๊ฒฐ๋ ๋์ ๋ชฉ๋ก์์ ์ฌ์ฉ์๋ ๋์ ๋์ถ, ๋์ ๋์ถ ์์ฝ์ ์งํ ํ ์ ์์ต๋๋ค.
์ฌ์ฉ์๊ฐ ๋์ ๋์ถ์ ์งํํ๋ฉด ๋์์ ๋์ถ ๊ฐ๋ฅ ์ฌ๋ถ๋ฅผ ์ ์ ์๋ Status ์ปฌ๋ผ์ POSSIBLE์์ IMPOSSIBLE๋ก ๋ณ๊ฒฝ๋ฉ๋๋ค. ํด๋น ๊ธฐ๋ฅ์ HTML์ Ajax HTTP ์์ฒญ์ ํตํด MySQL DB์ ์๋ ๋์ book_id์ ๋์์ ์ํ๋ฅผ ์ฆ์ ๋ณ๊ฒฝํ์ง๋ง, Elasticsearch๋ MySQL์ DB๋ฅผ Logstash๋ก ๊ฐ์ ธ์์ ์ธ๋ฑ์ฑํ๊ธฐ ๋๋ฌธ์ ์ด ๊ณผ์ ์์ ์๋ก ๋ชฉ๋ก์ ๋ถ๋ฌ์์ผ ํ ํ์์ฑ์ด ์์ต๋๋ค.
Logstash์ ๋ฐ์ดํฐ ๋ณ๊ฒฝ ์ถ์ ์ค์ ํ์์ฑ
๊ธฐ์กด Logstash๋ฅผ ํตํด MySQL์ DB๋ฅผ ์ ๋ฌํ์ฌ ์์ธํ๋ ๋ถ๋ถ์
๋๋ค. 709๋ง๊ฐ์ ๋์ ๋ฐ์ดํฐ๋ฅผ ํ๋ฒ์ ์ฝ์ด์ค๋ ๊ณผ์ ์์ JVM์ ๋ฉ๋ชจ๋ฆฌ ๋ถ์กฑ ํ์์ด ๋ฐ์ํ์ฌ 200๋ง๊ฐ์ฉ ๋ฉ๋ชจ๋ฆฌ์ ๋ก๋ํ๊ณ Elasticsearch๋ก ์ ๋ฌํ๋๋ก ๊ตฌ์ฑ๋์ด์์ต๋๋ค.
# sample.conf
input {
jdbc {
jdbc_connection_string => "jdbc:mysql://localhost:3306/team258"
jdbc_user => "inyongkim"
jdbc_password => "1234"
jdbc_driver_library => "/Users/inyongkim/.gradle/caches/modules-2/files-2.1/mysql/mysql-connector-java/8.0.25/f8b9123acd13058c941aff25f308c9ed8000bb73/mysql-connector-java-8.0.25.jar"
jdbc_driver_class => "com.mysql.cj.jdbc.Driver"
jdbc_paging_enabled => true
jdbc_page_size => 2000000 # ํ์ด์ง ํฌ๊ธฐ๋ฅผ ์กฐ์ ํ์ธ์.
statement => "SELECT * FROM book"
}
}
output {
elasticsearch {
hosts => ["localhost:9200"]
index => "book"
document_id => "%{book_id}"
}
}
ํ์ง๋ง ์ด ๋ถ๋ถ์์ ์ค์ ์ ๋ฌธ์ ์ ์ด ์์ต๋๋ค.
[Spring][258] MySQLโLogstashโElasticsearch ์ ๋๊ธฐํ ์ฒ๋ฆฌ์์ ๋ฐ์ํ๋ ๋ฌธ์
๊ณตํต - Controller, ๊ฒ์ ํค์๋์ ํ์ด์ง ์ฒ๋ฆฌ๋ฅผ ์ํ ํ์ด์ ๋ธ ๊ฐ์ฒด
์ฐ์ ๊ณตํต์ ์ผ๋ก ์ ์ฉ๋ ์ปจํธ๋กค๋ฌ ์ฝ๋๋ ๋ค์๊ณผ ๊ฐ์ต๋๋ค.
@Controller
@RequiredArgsConstructor
public class BooksViewController {
private final AdminCategoriesService adminCategoriesService;
private final AdminBooksService adminBooksService;
@GetMapping("/admin/booksManage")
public String adminBooksManageView( @RequestParam(defaultValue = "0") int page, @RequestParam(defaultValue = "10") int size, // ํ์ฌ ํ์ด์ง, ํ์ด์ง ํฌ๊ธฐ
@RequestParam(defaultValue = "bookId") String sort, @RequestParam(defaultValue = "ASC") String direction, // ์ ๋ ฌ ๊ธฐ์ค, ์ ๋ ฌ ๋ฐฉํฅ
@RequestParam(value = "keyword", required = false) String keyword, // ๊ฒ์ ํค์๋
@AuthenticationPrincipal UserDetailsImpl userDetails, Model model) {
long startTime = System.currentTimeMillis();//์คํ์๊ฐ ์ธก์
Sort.Direction sortDirection = Sort.Direction.fromString(direction);
PageRequest pageRequest = PageRequest.of(page, size, sortDirection, sort);
BooksPageResponseDto booksPageResponseDto =
adminBooksService.findBooksWithPaginationAndSearching(userDetails.getUser(), keyword, pageRequest);
model.addAttribute("books", booksPageResponseDto.getAdminBooksResponseDtos());
model.addAttribute("currentPage", page);
model.addAttribute("totalPages", booksPageResponseDto.getTotalPages());
long endTime = System.currentTimeMillis();
long durationTimeSec = endTime - startTime;
System.out.println(durationTimeSec + "m/s"); // ์คํ์๊ฐ ์ธก์
return "admin/booksManage";
}
ํ๋ก ํธ์๋๋ก๋ถํฐ keyword ๋ฅผ QueryString์ผ๋ก โฆ?keyword=abc์ ๊ฐ์ ํํ๋ก HTMLํผ์ผ๋ก๋ถํฐ ํน์ ๊ฐ๋ค์ ์ถ์ถ ํ ์ ์์ผ๋ฉฐ, HTTP์์ฒญ์ ๋ด์ ์ปจํธ๋กค๋ฌ๋ก ๋ฌธ์์ด์ ์ ๋ฌ ํ ์ ์์ต๋๋ค.
@RequestParam(value = "keyword", required = false) String keyword, // ๊ฒ์ ํค์๋
ํ์ด์ง ์ฒ๋ฆฌ๋ฅผ ์ํด์๋ ์์ JPA๋ฅผ ์ฌ์ฉํ๊ฒ ๋๋ฉด Total Count๋ฅผ ๋ฝ์์์ ํ์ฌ ๋ด๊ฐ ๋ช๋ฒ์งธ ํ์ด์ง์ธ์ง๋ฅผ ๊ณ์ฐํด์ ํ์๋ฅผ ํด์ฃผ์ด์ผ ํฉ๋๋ค. ๊ทธ๋ฐ๋ฐ ์ด๋ฐ ๊ณ์ฐ์ ๋ชจ๋ ์ง์ ๊ณ์ฐํ๋ ๋ฉ์๋, ๋ณ์๋ฅผ ์ง์ ์์ฑํ๊ณ ๋งค๊ฐ๋ณ์๋ก Service์ ์ ๋ฌํ๊ฒ ๋๋ฉด ์ฝ๋๊ฐ ๋ณต์กํด์ง๊ฒ ๋ฉ๋๋ค!ย ํ์ด์ ๋ธ ๊ฐ์ฒด๋ฅผ ์ฌ์ฉํ์ง ์๋ ๊ฒฝ์ฐ ์ถ๊ฐ๋์ด์ผ ํ๋ ์ฝ๋๋ ๋ค์๊ณผ ๊ฐ์ต๋๋ค.
// ์ง์ offset๊ณผ limit ๊ณ์ฐ
int offset = page * size;
int limit = size;
// ์ ์ฒด ํ์ด์ง ์ ๊ณ์ฐ ๋ฉ์๋
private int calculateTotalPages(long totalCount, int pageSize) {
return (int) Math.ceil((double) totalCount / pageSize);
}
BooksPageResponseDto booksPageResponseDto =
adminBooksService.findBooksWithPaginationAndSearching(userDetails.getUser(), keyword, offset, limit, sortDirection, sort);
[Spring][258] JPA โ JPQL โ QueryDSL ๋ณํ๊ณผ์
EC2ํธ ์ฐธ๊ณ ์๋ฃ
https://wickies.tistory.com/102
https://dev-coco.tistory.com/68
EC2 Console์์ MySQL ์ด๊ธฐ ์ค์ ์ฐธ๊ณ
https://blogshine.tistory.com/322
https://amor-fati.tistory.com/156
RDS ์ฐ๊ฒฐํ๊ธฐ ํฌํจํธ ์ฐธ๊ณ
https://velog.io/@u-nij/Spring-Boot-AWS-RDS-MySQL-%EC%97%B0%EB%8F%99%ED%95%98%EA%B8%B0
[Spring][AWS EC2]58 EC2๋ก ๋ฐฐํฌํด๋ณด๊ธฐ
๊ฒ์๊ธ์ ์์๋ ๋๊ธ ์ถ๊ฐํ๋ API
CommentController์์ ๊ณ์ POSTMAN์ผ๋ก blogId ํ๋ผ๋ฏธํฐ๋ฅผ ๋ชป๋ฐ์์จ๋ค.
// ๋๊ธ ์์ฑ by User API
@PostMapping("/comment")
public CommentResponseDto createComment(@RequestBody CommentRequestDto requestDto, HttpServletRequest req) {
User user = (User) req.getAttribute("user"); // ์ฌ์ฉ์ ์ ๋ณด๋ฅผ ์ถ์ถ
String blogId = req.getParameter("blogId"); // ํด๋ผ์ด์ธํธ์์ ์ ๋ฌํ blogId ๊ฐ์ ๊ฐ์ ธ์ด
// ๋๊ธ ์์ฑ์ ํ์ํ user์ blogId ์ ๋ณด๋ฅผ commentService์ ์ ๋ฌ
return commentService.createComment(requestDto, user.getUsername(), blogId);
}
๋ํ๋๋ ์ค๋ฅ๋ ๋๊ฐ์ง ์ ํ์ด์๋ค.
์ฒซ๋ฒ์งธ๋ NullPointerException์ผ๋ก blogId์์ฒด๊ฐ ์ ๋ฌ๋์ง ์๋ ์ํฉ,
2023-09-14T22:00:08.588+09:00 ERROR 35675 --- [nio-8080-exec-6] o.a.c.c.C.[.[.[/].[dispatcherServlet] : Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed: java.lang.NullPointerException: Cannot invoke "java.lang.Long.toString()" because "blogId" is null] with root cause
java.lang.NullPointerException: Cannot invoke "java.lang.Long.toString()" because "blogId" is null
at com.yzpocket.blog.controller.CommentController.createComment(CommentController.java:33) ~[main/:na]
์ด๊ฑด ๋๋ฌด ๋ปํ๋ค. ์ ์ฝ๋์์ blogId๋ฅผ req์ด๋ ๋ณ์, ์ด๋ ๊ณง HttpServletRequest์์ ๋ฐ๊ณ ์๋๋ฐ ์ฌ๊ธฐ ์์๋ฆฌ๊ฐ ์๋ค. ์๋ณธ ๊ธ์ id๋ ํด๋ผ์ด์ธํธ์์ ํ๋ผ๋ฏธํฐ๋ก ๋ฐ์์ผ ํ๋ค. ๊ทธ๋์ ์๋ ์ฝ๋์๊ฐ์ด ๋ณ๊ฒฝํ๋ค.
// ๋๊ธ ์์ฑ by User, blogID API
@PostMapping("/comment")
public CommentResponseDto createComment(@RequestBody CommentRequestDto requestDto, @RequestParam("blogId") Long blogId, HttpServletRequest req) {
log.info(blogId.toString());
User user = (User) req.getAttribute("user"); // ์ฌ์ฉ์ ์ ๋ณด๋ฅผ ์ถ์ถ
String blogIdStr = (blogId != null) ? blogId.toString() : null;
// ๋๊ธ ์์ฑ์ ํ์ํ user์ blogId ์ ๋ณด๋ฅผ commentService์ ์ ๋ฌ
log.debug(blogIdStr); // ๋๋ฒ๊ทธ ๋ ๋ฒจ์ ๋ก๊ทธ๋ก ์คํ ํธ๋ ์ด์ค ๋ฐ ๋๋ฒ๊ทธ ์ ๋ณด๋ฅผ ์ถ๋ ฅ
return commentService.createComment(requestDto, user.getUsername(), blogIdStr);
}
[Spring][JPA]55 ๋๊ธ๊ธฐ๋ฅ ๊ตฌํ ์ค ํธ๋ฌ๋ธ์ํ
- POSTMAN ๋ฒ๊ทธ? ์ ํ ์ํ
๋์ง ์์์ ํ๋ฌดํ๋ค. Required request parameter 'Argumentsโ for method parameter type Long is not present
CascadeType.REMOVE
โข
CASCADE์ REMOVE ์ต์
์ ์ ์ฉํ๋ฉด ํด๋น Entity ๊ฐ์ฒด๋ฅผ ์ญ์ ํ์ ๋ ์ฐ๊ด๋ Entity ๊ฐ์ฒด๋ค์ ์๋์ผ๋ก ์ญ์
โข
ํ์ง๋ง REMOVE ์ต์
๊ฐ์ ๊ฒฝ์ฐ ์ฐ๊ด๋ Entity์ ๊ด๊ณ๋ฅผ ์ ๊ฑฐํ๋ค๊ณ ํด์ ์๋์ผ๋ก ํด๋น Entity๊ฐ ์ญ์ ๋์ง๋ ์๋๋ค.
@Entity
@Getter
@Setter
@Table(name = "users")
public class User {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
@OneToMany(mappedBy = "user", cascade = {CascadeType.PERSIST, CascadeType.REMOVE})
private List<Food> foodList = new ArrayList<>();
public void addFoodList(Food food) {
this.foodList.add(food);
food.setUser(this);// ์ธ๋ ํค(์ฐ๊ด ๊ด๊ณ) ์ค์
}
}
orphanRemoval = true
@Entity
@Getter
@Setter
@Table(name = "users")
public class User {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
@OneToMany(mappedBy = "user", cascade = CascadeType.PERSIST, orphanRemoval = true)
private List<Food> foodList = new ArrayList<>();
public void addFoodList(Food food) {
this.foodList.add(food);
food.setUser(this);// ์ธ๋ ํค(์ฐ๊ด ๊ด๊ณ) ์ค์
}
}
orphanRemoval ์ต์
๋ REMOVE ์ต์
๊ณผ ๋ง์ฐฌ๊ฐ์ง๋ก ํด๋น Entity ์ฆ, Robbie Entity ๊ฐ์ฒด๋ฅผ ์ญ์ ํ๋ฉด ์ฐ๊ด๋ ์์ Entity๋ค์ด ์๋์ผ๋ก ์ญ์ ๋๋ค.
โข
orphanRemoval์ด๋ REMOVE ์ต์
์ ์ฌ์ฉํ ๋ ์ญ์ ํ๋ ค๊ณ ํ๋ ์ฐ๊ด๋ Entity๋ฅผ ๋ค๋ฅธ ๊ณณ์์ ์ฐธ์กฐํ๊ณ ์๋์ง ์๋์ง๋ฅผ ๊ผญ ํ์ธ ํ์
[Spring][JPA]54 ๊ณ ์ Entity ์ญ์ (๊ฒ์๊ธ-๋๊ธ)

[Spring][JPA]53 Fetch Type, ์ง์ฐ๋ก๋ฉ(LAZY), ์ฆ์๋ก๋ฉ(EAGER)
JPAย Auditing์ด๋?
Java์์ ORM ๊ธฐ์ ์ธ JPA๋ฅผ ์ฌ์ฉํ์ฌ ๋๋ฉ์ธ์ ๊ด๊ณํ ๋ฐ์ดํฐ๋ฒ ์ด์ค ํ
์ด๋ธ์ ๋งคํํ ๋ ๊ณตํต์ ์ผ๋ก ๋๋ฉ์ธ๋ค์ด ๊ฐ์ง๊ณ ์๋ ํ๋๋ ์ปฌ๋ผ๋ค์ด ์กด์ฌํ๋ค. ๋ํ์ ์ผ๋ก ์์ฑ์ผ์, ์์ ์ผ์, ์๋ณ์ ๊ฐ์ ํ๋ ๋ฐ ์ปฌ๋ผ์ด ์๋ค.
๋๋ฉ์ธ๋ง๋ค ๊ณตํต์ผ๋ก ์กด์ฌํ๋ค๋ ์๋ฏธ๋ ๊ฒฐ๊ตญ ์ฝ๋๊ฐ ์ค๋ณต๋๋ค๋ ๋ง์ด๋ฉฐ ์ค๋ณต์ ์ ๊ฑฐํ๋ ๊ฒ์ ํ๋ก์ ํธ์ ์ ์ง๋ณด์์ ์ธก๋ฉด์์ ํต์ฌ์ ์ธ ์ฌ์์ด๋ค.
๋ฐ์ดํฐ๋ฒ ์ด์ค์์ย ๋๊ฐ, ์ธ์ ํ์๋์ง ๊ธฐ๋ก์ ์ ๋จ๊ฒจ๋์์ผ ํ๋ค. ๊ทธ๋ ๊ธฐ ๋๋ฌธ์ ์์ฑ์ผ, ์์ ์ผ ์ปฌ๋ผ์ ๋๋จํ ์ค์ํ ๋ฐ์ดํฐ๋ค.
๊ทธ๋์ JPA์์๋ย Audit์ด๋ผ๋ ๊ธฐ๋ฅ์ ์ ๊ณตํ๊ณ ์๋ค. Audit์ย ๊ฐ์ํ๋ค, ๊ฐ์ฌํ๋ค๋ผ๋ ๋ป์ผ๋ก Spring Data JPA์์ ์๊ฐ์ ๋ํด์ ์๋์ผ๋ก ๊ฐ์ ๋ฃ์ด์ฃผ๋ ๊ธฐ๋ฅ. ๋๋ฉ์ธ์ ์์์ฑ ์ปจํ
์คํธ์ ์ ์ฅํ๊ฑฐ๋ ์กฐํ๋ฅผ ์ํํ ํ์ update๋ฅผ ํ๋ ๊ฒฝ์ฐ ๋งค๋ฒ ์๊ฐ ๋ฐ์ดํฐ๋ฅผ ์
๋ ฅํ์ฌ ์ฃผ์ด์ผ ํ๋๋ฐ, audit์ ์ด์ฉํ๋ฉด ์๋์ผ๋ก ์๊ฐ์ ๋งคํํ์ฌ ๋ฐ์ดํฐ๋ฒ ์ด์ค์ ํ
์ด๋ธ์ ๋ฃ์ด์ค ์ ์๋ค.
์์กด์ฑ ์ถ๊ฐ
๊ธฐ๋ณธ์ ์ผ๋ก ์คํ๋ง ๋ถํธ์์ gradle๋ก ์์กด์ฑ์ ๊ด๋ฆฌํ๊ฒ ๋ ๊ฒฝ์ฐย spring-boot-starter-data-jpa๋ง ์ถ๊ฐํด๋ Audit์ ํ๋๋ฐ๋ ๋ฌธ์ ๊ฐ ์๋ค. ์ด ๋ง์ Spring-Data-JPA ์์ ํด๋น ๊ธฐ๋ฅ์ด ์๋ค๋ ๋ง๊ณผ ๊ฐ๋ค.
dependencies {
...
//SpringBoot JPA
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
...
}
[Spring][JPA]52 JPA Auditing์ด๋? SpringBoot์์ ๊ตฌ์ฑ ๋ฐ ํ์ฑํ ์ํค๊ธฐ ์ค์ต
๋ค๋์ผ (N:1) ๊ด๊ณ ๋จ๋ฐฉํฅ๊ณผ ์๋ฐฉํฅ
1. ๋ค๋์ผ ๊ด๊ณ ๋จ๋ฐฉํฅ
๋ค๋์ผ (N:1) ๊ด๊ณ (๋จ๋ฐฉํฅ)
โข
์์: ํ๋์ ์ฃผ๋ฌธ(Order)์ ํ๋์ ๊ณ ๊ฐ(Customer)์ ์ํฉ๋๋ค.
โข
์ค๋ช
:
โข
Spring JPA ์ด๋
ธํ
์ด์
@Entity
public class Order {
// ๋ค๋ฅธ ํ๋๋ค...
@ManyToOne
@JoinColumn(name = "customer_id")
private Customer customer;
// Getter์ Setter...
}
@Entity
public class Customer {
// ๋ค๋ฅธ ํ๋๋ค...
// Getter์ Setter...
}
2. ๋ค๋์ผ ๊ด๊ณ ์๋ฐฉํฅ
[Spring][JPA]49_2 Entity ์ฐ๊ด๊ด๊ณ ํ๊ฐ๋ ค์ ์์๋ฅผ ํตํด ํ๋ฒ ๋ ์ ๋ฆฌ
์ฐ๊ด๊ด๊ณ ๋งคํ์ ๊ณ ๋ ค์ฌํญ 3๊ฐ์ง
โข
๋ค์ค์ฑ
โข
๋จ๋ฐฉํฅ, ์๋ฐฉํฅ
โข
์ฐ๊ด๊ด๊ณ์ ์ฃผ์ธ
๋ค๋์ผ[N:1] ์ฐ๊ด๊ด๊ณ
๋ค๋์ผ[N:1] ๋จ๋ฐฉํฅ
โข
๊ฐ์ฅ ๋ง์ด ์ฌ์ฉํ๋ ์ฐ๊ด๊ด๊ณ
โข
๋ค๋์ผ์ ๋ฐ๋๋ ์ผ๋๋ค
[Spring][JPA]49 Entity ์ฐ๊ด๊ด๊ณ N:1, 1:N, 1:1, N:M ๋งตํ ๋ฐ ๋ฐฉํฅ
๊ฐ์ฅ ์ค์ํ ๊ฒ์ ๋ชฐ์์น๋ฉฐ ๋ฐฐ์ด ํ ์ฃผ
์ฌ์ค ๋๋ฌด ๋ง์ ์ ๋ณด๊ฐ ํ๋ฒ์ ๋ค์ด์๋ค. ๊ทธ๋๋ง ์ดํดํ๋ฉด์ ๋์ด๊ฐ๊ณ ์ ์กฐ๊ธ ์๊ฐ์ ๊ฑธ๋ฆฌ๊ณ ์์ง๋ง, ์๊ฐ๋ณด๋ค ๋๋ฌด ์ค์ํ ๊ธฐ๋ฅ ๊ตฌํ๊ณผ ๊ฐ๋
, ์ค์ต์ ๋์์ ์งํํ๋ค๋ณด๋ ์๊ฐ์ฒ๋ผ ์ข ๋ง ๋์ด๊ฐ๋ ๊ฒ์ด ๋๋๋ค. ๊ทธ๋งํผ ์ค์ํ ๋ถ๋ถ์ธ ๊ฒ์ ์๊ฐํ๋ฉด์ ์ง์งํ๊ฒ ๊ณต๋ถํ๊ณ , ์ต๋ํ ๋ง์ ๊ฒ๋ค์ ๋จธ๋ฆฌ์์ ์ง์ด๋ฃ์ผ๋ ค๊ณ ๋
ธ๋ ฅํ๋ค.
๊ทธ๋ฆฌ๊ณ ์๊ฐ๋ณด๋ค Java์ ๊ฐ์ฒด ์งํฅ ํ๋ก๊ทธ๋๋ฐ์ ๊ฐ๋
์ด ๋ฐํ์ง ์์ ์ํ์๋ค๋ฉด ์ ๋ง ํฌ๊ธฐํ์์ง๋ ๋ชจ๋ฅผ ๊ฒ ๊ฐ๋ค๋ ์๊ฐ์ด ๋ค์ด์ ์๋์ ํ์จ์ ์ฌ๋ฉด์ ๋ชจ๋ ๊ฒ์ ์ธ์ฐ๊ฑฐ๋ 100%๊น์ง ํก์ ํ์ง ๋ชปํ๋๋ผ๋, ๋ด๊ฐ ๊ฐ์๋ฅผ ๋ณด๊ณ , ์ค์ต์ ํ๊ณ , ํ
์คํธ๋ฅผ ํ ๋, ๊ทธ ํ๋ฆ์ ์ดํด ํ ์ ์์ ์์์ ๊ฐ์ฌํ๋ฉด์ ๊ณต๋ถ๋ฅผ ์งํํ ํ์ฃผ์๋ค.
์ค์ํ ์ค์ต์ด ๋ง์ ๋งํผ ๋ง์ ๋ฐ์ ์ด ์์๊ณ , ์ ๋ฆฌ๋ฅผ ๋ ์ํ๊ณ ์ ๋
ธ๋ ฅ
์ฐ์ ๋ธ๋ก๊ทธ์ ์ํ๋ฅผ ๊พธ์คํ ๊ด๋ฆฌํ๋ฉฐ ํ ์ฃผ๋ฅผ ์์ํ์๋ค. ๋งค์ผ TIL์ 3~4๊ฐ ์์ฑํ๋ ๋๋ก์จ๋ ํ๋ซํผ์ ์์ ์ ์ธ ์๋น์ค๊ฐ ์๋, ๋ด๊ฐ ๋ด ๋ฐ๋ชฉ์ ๋ถ์ก๋ฏ์ด ๋ถ์์ ํ ์ด๋ณด ๊ฐ๋ฐ์์ ๋ฐฐํฌ๋ฅผ ์์งํ์๊ณ , ์ญ์๋ ๋ฐฐํฌ์ ์ต์ ํ ๊ด๋ฆฌ์ ๋ฌธ์ ๊ฐ ์๊ธฐ๊ณ ์์๋ค. ๋๋ ์ด ์ํฉ์ ์ต๋ํ ์ ์ฐํ๊ฒ ๋์ด๊ฐ๊ธฐ ์ํด์ ์ฌ๋น ๋ฅด๊ฒ ๊ธฐ๋ณธ์ ์ธ ์ด๋ฏธ์ง ์ต์ ํ, ๋ธ๋ก๊ทธ์ ๋น๋ํ์ ์ค๋ฒํ๋ก์ฐ์ ์์ธ ๋ฑ์ ํ์
ํ๊ธฐ ์์ํ๊ณ , ์ 2์ ๋ฐฉ์์ธ ๊ธ๋ค์ ๋ค๋ฅธ DB๋ก ๋ฐฑ์
ํ๋ ์ฐจ์ ์ฑ
์ ๋ง๋ จํ๋ค. ๋คํํ ๋
ธ๋ ฅ ๋์ ๋ธ๋ก๊น
์ ์กฐ๊ธ ์์ ์ ์ผ๋ก ๋ณํํ๊ณ , ์ผ์ถ ๊ทธ ๋ฌธ์ ์ ๋ค์ ์ค๊ณฝ์ด ์กํ๊ณ ์๋ค. ๊ทผ๋ณธ์ ์ธ ํด๊ฒฐ์ ์์ง ๋ฉ์์ง๋ง, ์ด์์ ์ธ ๋ฉด์์ ๊พธ์คํ ๊ธ์ ์
๋ก๋ ํ ๋ฃจํด์ ๋ฐ๊ฒฌํ๋ค๋๊น?
๋ํ์ฌ ํฌ๊ฒ ๋ณ๊ฒฝ๋ ๊ฒ์ Github์ ์ ๊ทน์ ์ผ๋ก ๊ด๋ฆฌํ๊ณ ์๋ ๊ฒ์ด๋ค. ๊ธฐ๋ก๊ณผ ๋จ๋๊ฒ๋ค์ ๋ํด์ ์ง์งํ๊ฒ ์๊ฐํ๊ณ ์๋ค. ์ด๋ป๊ฒ ๋ณด๋ฉด ๊พธ์คํจ๊ณผ ๋
ธ๋ ฅ์ ๊ฐ๊ด์ ์ผ๋ก ํ๊ฐ ํ ์ ์๋ ์งํ๊ฐ ์ด๊ฒ๋ค ๋ฟ์ด๋ผ, ๋ง๋ง ์ํ๊ณ ๊ธ๋ง ์์ด๋ค๊ณ ํด์ ํด๊ฒฐ ๋ ๋ฌธ์ ๊ฐ ์๋ ๊ฒ ๊ฐ์๋ค. ๋ํ GIthub์ ๊พธ๋ฉฐ๋๊ฐ๋ฉด์ ๋ฐฐ์ด๊ฒ์ โ๋๋ฅผ ๋ธ๋๋ฉโ ํ๋ ๋ฐฉ๋ฒ์ด์๋ค. ํ๊ณผ ์ด ๋ถ๋ถ์ ๋ํด์ ์๋ก์ ๊ฐ์น๊ด์ ๋ํด์ ์ค๋ ์๊ฐ๋์ ์ง์งํ ์ฌ๋ฌ ์ด์ผ๊ธฐ๋ฅผ ๋๋๊ฒ ๋์๊ณ , 1์ผ 1์ปค๋ฐ ์ด์์ผ๋ก ๋ด ์์งํ ๊ณต๋ถ์ ์ด์ ์ ๋ณด์ฌ ์ค ์ ์๋ ๊ฐ๊ด์ ์ธ ์๋ฃ์ ์ค๋น๋ฅผ ๋์์ ํ๊ณ ์๊ธฐ๋ ํ๋ค. ๋ฌผ๋ก ์๋์ ์ธ๊ฒ์ด ์๋๋ค. ๋๋ ์๋ ์ด๋ฐ ๊ฒ๋ค์ ๊พธ๋ฉฐ๋๊ฐ๊ณ ์์ฑํ๊ณ ์ ๋ฆฌํ๋ ๊ฒ์ ์ฆ๊ธฐ๋๋ฐ, ๋ฐฉ๋ฒ์ ๋ชฐ๋๋ ๊ฒ์ด๋ค. ๊ทธ๋ฐ๋ฐ ์ข์ ์ฌ๋ก๋ค์ ์ฐพ์๋ณด๋ฉด์ ํ๋ค๋ณด๋ ๋๋ฌด ์ฌ๋ฐ๋ค. ๋งํฌ๋ค์ด ๋ฌธ๋ฒ๋ ๋ญ๊ฐ ์ฌ๋ฐ๋๊ฒ ์์ ๊ฒ ๊ฐ์ ๋๋?
์ธ์์ ๋ฉํ , ๊ฐ์ฅ ์กด๊ฒฝํ๋ ์ธ๋ฌผ?
๋๋ ์น ํ์ ๋จ์ํ ๊ฐ์กฑ, ํ์ ์
์ฅ์ผ๋ก๋ง ํ์ ๋ฐ๋ผ๋ณด์ง ์๋๋ค. ์ด๋ป๊ฒ ๋ณด๋ฉด IT์
๊ณ์ ๋์ ๋ฐฐ์ธ ํ์ ํ๋๋ค์ ์กด๊ฒฝํ๊ณ ์๊ณ , ์ด๋ฆด๋๋ถํฐ ๊ทธ ํน์ ์ ์ถ์ง๋ ฅ๊ณผ ๋์ง๊น, ์ง์์ ๊ฐ์ ์ ์ผ๋ก ์ํฅ์ ๋ฐ์์ค๋ฉด์ ์๋๊ณ ํ์ฌ๋ ์ํฅ์ ์ฃผ๊ณ ๋ฐ๋๋ค. ์ด๊ฒ์ ๋ด๊ฐ ๊ฐ์ ํ๊ฒฝ์ ์ผ๋ก ๋งค์ฐ ์ข์ ์ด์ ์ด๋ผ๊ณ ์๊ฐํ๋ค. ์ฐ๋ฆฌ ํ์ ๋์๊ฒ ๋จ์ํ ๊ณ ๊ธฐ๋ฅผ ์ฃผ์ง ์์๋ค. ๋ด๊ฐ ์ด์๊ฐ ์ ์๋๋ก ๊ณ ๊ธฐ ์ก๋ ๋ฒ์ ์๋ ค์ฃผ๊ณ , ์ง๋ํ๋ ์๋ฒ์ง์ ์ญํ ์ ํ๋ฉด์ ๋ํ ๋ณธ์ธ๋ ํ๋๊ณผ ๊ฒฐ๊ณผ๋ก ๋ณด์ฌ์ฃผ๋ฉด์ ๋์๊ฒ ํฐ ๋๊ธฐ๋ถ์ฌ์ ๊ท๊ฐ์ด ๋๋ ์ธ๋ฌผ์ด๋ค. ๊ทธ๋์ ๋๋ ์ธ์์์ ์ ์ผ ์กด๊ฒฝํ๋ ์ฌ๋์ด ํ์ผ ์ ๋ฐ์ ์๋ค. ๊ฒฐ๊ตญ ๋ด๊ฐ ๊ฐ์ง ๋ผ์๊น์ง ๊น์ ๊ณ ๋ฏผ์ด๋ ์ง๋ก, ์ถ์ ๊ฑฑ์ ์ ๋ํด์ ๋ชจ๋ ๊ฒ์ ํธ์ด๋ฒ๋ฆฌ๋ฉด์ ์ด์ผ๊ธฐ ํ ์ฌ๋, ๊ทธ๋ผ๊ณผ ๋์์ IT ์
๊ณ์ ๋์ ๋ฐฐ์ ์์ ๋กญ๊ฒ ์ด์ผ๊ธฐ๋ฅผ ๋๋ ์ ์๋ ์ฌ๋์ด ์นํ์ด๋ผ๋ ๊ฒ์ ์์ฒญ ํฐ ํ์ด์ด๋ผ๊ณ ์๊ฐํ๋ค. ์ฃผ๋ณ ๋ค๋ฅธ ํ์ ๊ด๊ณ์ ๋นํด์ ์ฐ๋ฆฌ ํ๊ณผ ๋์ ๊ด๊ณ๋ ์ด๋ฆด๋๋ถํฐ ์์ฃผ ๋ง์ด ์นํ ํธ์ด๊ณ , ๊ทธ ์ฐ์ ๊ฐ ๋ณดํต 20๋ ์คํ๋ฐ์ฏค ์๋ก ์ด์๊ฐ๋๋ผ ๋ฐ์ ํ
๋ฐ, ์ฐ๋ฆฌ ํ์ ๋ ๋์ด๋ฅผ ๋จน์ด๊ฐ๋ฉด์๋ ์๋ก ๋์ฑ ๊ด๊ณ๊ฐ ์ ์ง๋๊ณ ์คํ๋ ค ํจ๊ป ์ฑ์ฅํ ๋์ด๋์ ๊ฑธ๋ง๊ฒ ์์ฐ์ ์ด๊ณ ๋ฏธ๋ ์งํฅ์ ์ธ ๊ฐ์น๊ด, ์ง์์ ๋ํ ์ฑ
์๊ฐ์ ๊ณต์ ํ๋ ๊ฒ์ผ๋ก ๋ฐ์ ํด๊ฐ๊ณ ์๋ค. ๋ ๋ํ ํ์ฌ ์ฒญ์ถ์ ๊ทธ ์ฑ
์๊ฐ์ ๋์ด์ฃผ๊ณ ํจ๊ปํด์ผ ํ๋ค. ๊ทธ๋ฆฌ๊ณ ์ฑ์ํ ์ด๋ฅธ์ผ๋ก ํจ๊ป ๋ฑ๋ฐํด ๋๊ฐ์ผ ํ๋ค. ๋ ๊ฐ์ฅ ๊ฐ๊น์ด ํ์ ๋ฐ์์ทจ์ ์์์ด ๋ง์ ํ๊ณ , ๋ํ๋ฅผ ํตํด ์ง์
์ ๋ํ ์ฑ
์๊ฐ, ์ ์ฑ์ ์ฐพ๋ ๋ฐฉ๋ฒ, ์ถ์ ๋ชฉํ๋ฅผ ์ค์ ํ๋ ๊ฒ, ์ด๊ฒ๋ค์ ๊ณง ์ธ์์ด๋ ๋จ์ด๋ฅผ ๋ฐฐ์ด๋ค. ๋๋ฌด๋๋ ํฐ ํ์ด์ด๋ค. Spring์ ํตํด ๊ฐ๋ฐ์๋ค์๊ฒ ๋ด์ด ์จ๊ฒ ์ฒ๋ผ ๋๋ ๋น์ฅ์ ํ๋ค๊ณ ์ด๋ ต๊ฒ ๋๊ปด์ง์ง๋ง Spring์ ๋ฐฐ์ ๋๊ฐ๋ฉด์ ๋ด ์ธ์์๋ ๋ด์ด ์ค๊ธธ ๊ธฐ๋ํ๊ณ ์๋ค.
[Spring]50 ์ค์ํ ๊ธฐ๋ฅ์ ๋ง์ด ๋ฐฐ์ด ํ ์ฃผ, ๋๋๊ฒ๋ ๋ง์ ํ ์ฃผ, ๋๊ธฐ๋ฅผ ๋ค์ ๋ถ์ฌํ๋ ํ์ฃผ.
์ ์์ฝ, ํ ์์
JPA๋ ์๋ฐ ์ง์์ ORM ๊ธฐ์ ์ ๋ํ API ํ์ค ๋ช
์ธ์ด๋ฉฐ
Hibernate๋ JPA์ ๊ตฌํ์ฒด์ด๊ณ , ๋ด๋ถ์ ์ผ๋ก JDBC๋ฅผ ์ด์ฉํ๋ค.
Spring Data JPA๋ JPA๋ฅผ ์ฌ์ฉํ๊ธฐ ์ฝ๊ฒ ์คํ๋ง์์ ์ ๊ณตํ๋ ๋ชจ๋๋ก ๋ด๋ถ์ ์ผ๋ก JPA ๊ตฌํ์ฒด๋ฅผ ์ด์ฉํ๋ค.
JPA์ด์ ์ ๋ฐฐ๊ฒฝ, SQL Mapper๋?
SQL Mapper๋ ๊ฐ๋ฐ์๊ฐ ์ง์ SQL๋ฌธ์ ์์ฑํด ๋ฐ์ดํฐ๋ฒ ์ด์ค ๋ฐ์ดํฐ๋ฅผ ๋ค๋ฃจ๋ Persistence Framework์ด๋ค. ๊ฐ์ฒด์ ํ
์ด๋ธ ๊ฐ์ ๊ด๊ณ๋ฅผ ์ง์ ๋งคํํ๋ ๊ฒ์ด ์๋, SQL๋ฌธ์ ์คํํด ์ฟผ๋ฆฌ ์ํ ๊ฒฐ๊ณผ๋ฅผ ์ด๋ค ๊ฐ์ฒด์ ๋งคํํ ์ง ๋ฐ์ธ๋ฉ ํ๋ ๋ฐฉ๋ฒ์ด๋ค.
๋ฐ๋ผ์ DBMS์ ์ข
์์ ์ธ ๋ฐฉ๋ฒ์ด๋ผ๊ณ ํ ์ ์๋ค. ๋ํ์ ์ผ๋ก MyBatis๊ฐ ์๋ค. ์ฌ๊ธฐ ๋จ๊ณ๊น์ง๋ JDBC Template๋ฅผ ๊ฐ์ ํด์ ์ฌ์ฉํ์ง๋ง, ๊ฒฐ๊ตญ SQL๋ฌธ์ ์ง์ ์์ฑํด์ผ๋๋ค๋ ๋จ์ ์ด ์๋ค.
์ฅ์
[Spring][JPA]51 JPA๋? MyBatis(SQL Mapper)๋ถํฐ ์์ ํต์ฌ์์ฝ
developers.naver.com
๋ค์ด๋ฒ์ ๊ฐ๋ฐ์ ํ๋ซํผ์ ํตํด์๋ ๋ค์ด๋ฒ๊ฐ ๋ง๋๋ ๊ฐ์ข
Open API๋ฅผ ํ์ฉ ํ ์ ์๋ค.
๊ธฐ๋ณธ์ ์ผ๋ก ๋ค์ด๋ฒ์ ๋ถ๊ฐ ๊ธฐ๋ฅ๋ค๊ณผ ์ฐ๊ฒฐ๋์ด ์์ผ๋ฉฐ ๋ชฉ๋ก์ ๋ค์๊ณผ ๊ฐ๋ค.
์ฐ์ ๋์ ๋ค์ด์ค๋ ๊ธฐ๋ฅ๋ค
๊ฒ์, ๋ค์ด๋ฒ ๋ก๊ทธ์ธ, ๋ค์ด๋ฒ ํ์ ํ๋กํ ์กฐํ, Papago ๋ฒ์ญ, ๊ฒ์์ด ํธ๋ ๋ ์กฐํ, ์ผํ์ธ์ฌ์ดํธ ๋ถ์ผ๋ณ ํธ๋ ๋ ์กฐํ, ์บก์ฐจ, ์บ๋ฆฐ๋ ๊ธฐ๋ฅ์ ์น ์๋น์ค์์ ์์ฃผ ์ฌ์ฉ๋ ๊ฒ ๊ฐ๋ค. ์ด์ค์ ๋ค์ด๋ฒ ๊ฒ์ ์์ฒญ API๋ฅผ SpringBoot์ผ๋ก ๊ตฌํํ๊ณ ์ ํ๋ค.
์์ ์ CLOVA ๊ธฐ๋ฅ๋ค์ ์ ์ ์ดํด๋ณธ์ ์ด ์์๋๋ฐ ๋ณด๋ค ๋ง์ ๊ธฐ๋ฅ๋ค์ด ์์๋ค. AI๊ด๋ จ ๊ธฐ๋ฅ๋ ์์์ผ๋ฉฐ ๋ฐ์ดํฐ์ฌ์ด์ธ์ค ๋ถ์ผ์ ๊ธฐ๋ฅ๋ค๋ ์์๋๊ฒ์ผ๋ก ๊ธฐ์ตํ๋ค. ์ข
๋ฅ๋ ์๊ฐ๋ณด๋ค ์์ฃผ ๋ง์์ ์ต๋ณตํฉ ์์ด๋์ด๋ฅผ ๊ตฌ์ ํ ๋ ํต์ฌ์ ์ผ๋ก ์ฌ์ฉ ๋ ๋ฏ ํ๋ค.
๊ทธ ์ธ ๋ค์์ ์ฃผ์๊ฒ์ API๋ ์ ๋ช
ํ๊ฒ ๊ฐ๊ณ , ์๋ง๋ CLOVA์์ ์๋๋ฏ ํ๋ฐ ์ง๋ ๋ฐ GPS๊ธฐ๋ฅ ๋ฑ์ด ์์ ๊ฒ์ด๋ค. ์นด์นด์ค๋ ์ฃผ๋ ฅ ์๋น์ค๋ค๊ณผ ์ฐ๊ด๋ Open API๋ฅผ ๊ฐ์ง๊ณ ์์ผ๋ฉฐ ์ค๋ณต๋๋ ๊ฒ๋ค๋ ๋ง๋ค. ์ฌ์ค ์ด๋ค ๊ธฐ๋ฅ์ ์ ํ ํ๋๊ฒ์ ์ด๋ค ์๋น์ค๋ฅผ ๋ง๋ค ์ง ๊ณํํ๊ณ ๊ธฐํ ๋จ๊ณ์์ ์๊ฐํ ๋ฌธ์ ์ด๋ค.
[Spring]48 NAVER โSearchingโ Open API Test
RestTemplate์ด๋ ๋ฌด์์ผ๊น?
โข
์ง๊ธ๊น์ง๋ Client ์ฆ, ๋ธ๋ผ์ฐ์ ๋ก๋ถํฐ ์์ฒญ์ ๋ฐ๋ ์๋ฒ์ ์
์ฅ์์ ๊ฐ๋ฐ์ ์งํํด์๋ค.
โข
์๋น์ค ๊ฐ๋ฐ์ ์งํํ๋ค๋ณด๋ฉด ๋ผ์ด๋ธ๋ฌ๋ฆฌ ์ฌ์ฉ๋ง์ผ๋ก๋ ๊ตฌํ์ด ํ๋ ๊ธฐ๋ฅ๋ค์ด ๋ฌด์ํ ๋ง์ด ์กด์ฌํ๋ค.
โข
์๋ฅผ ๋ค์ด ์ฐ๋ฆฌ์ ์๋น์ค์์ ํ์๊ฐ์
์ ์งํํ ๋ ์ฌ์ฉ์์ ์ฃผ์๋ฅผ ๋ฐ์์ผ ํ๋ค๋ฉด? GPS๋ฅผ ํตํ ์์น ์๋ฃ๊ฐ ํ์ํ๋ค๋ฉด? ๋ฑ๋ฑ
์ด๋ด ๋, ๋ด๊ฐ ๊ฐ๋ฐํ๋ ์๋ฒ๋ Client์ ์
์ฅ์ด ๋์ด ์๋ก Kakao ์๋ฒ์ ์์ฒญ์ ์งํํด์ ๋ฐ์์ผ ํ๋ค.
โข
Spring์์๋ ์๋ฒ์์ ๋ค๋ฅธ ์๋ฒ๋ก ๊ฐํธํ๊ฒ ์์ฒญํ ์ ์๋๋ก RestTemplate ๊ธฐ๋ฅ์ ์ ๊ณต
๊ฒฐ๋ก ์ ์ผ๋ก ๋ด ํฌ์ง์
๋ํ ํด๋ผ์ด์ธํธ ์
์ฅ์ด ๋์ด์ ์ด๋ ํ ๋ฐ์ดํฐ๋ฅผ ๋ฐ์์์ผ ํ๋ค.
โข
์น ํ์ด์ง(ํ๋ก ํธ์๋, ํด๋ผ์ด์ธํธ) โ (์๋ฒ)๋์ ๋ฐฑ์๋ ์๋ฒ
[Spring]47 RestTemplate ์ค์ต Client-Server ํ
์คํธ(Open API์ ์๋ฆฌ ์ดํด๋ฅผ ์ํด)
UUID ํ์ค ์ ๋ฆฌ
โข
๋คํธ์ํฌ ์์์ ๊ณ ์ ์ฑ์ด ๋ณด์ฅ๋๋ id๋ฅผ ๋ง๋ค๊ธฐ ์ํ ํ์ค ๊ท์ฝ
โข
UUID๋ Universally Unique IDentifier์ ์ฝ์ด์ด๊ณ ๋ฒ์ฉ ๊ณ ์ ์๋ณ์๋ผ๊ณ ํ๋ค.
UUID ์ข
๋ฅ
โข
์ข
๋ฅUUID๋ฒ์ ์ 1,3,4,5๊ฐ ์๋ค. ์ด์ค ๊ฐ์ฅ ๋ง์ด ์ฐ์ด๋ ๋ฒ์ ์ 1๋ฒ์ ๊ณผ 4๋ฒ์ ์ด๋ค.
โข
1๋ฒ์ ๊ฐ์ ๊ฒฝ์ฐ, ํ์์คํฌํ๋ฅผ ๊ธฐ์ค์ผ๋ก ์์ฑ๋๋ค. (ํธ์คํธ ID, ์ํ์ค ๋ฒํธ ๋ฐ ํ์ฌ ์๊ฐ์ผ๋ก UUID ๋ฐ๊ธ)
โข
4๋ฒ์ ๋ ๋๋ค ์์ฑ (๋ฌด์์ UUID ์์ฑ) ์ด๋ผ๊ณ ์๊ฐํ๋ฉด ๋๋ค.
[Spring][JPA]42 UUID?
ํต์ฌ ์ด๋
ธํ
์ด์
@Entity
โข
@Entity ์ด๋
ธํ
์ด์
์ด ๋ถ์ ํด๋์ค๋ JPA๊ฐ ๊ด๋ฆฌํ๋ ์ํฐํฐ์ด๋ค
โข
JPA๋ฅผ ์ฌ์ฉํด์ ํ
์ด๋ธ๊ณผ ๋งคํํ ํด๋์ค๋ @Entity ์ด๋
ธํ
์ด์
์ด ํ์๋ค
โข
๊ธฐ๋ณธ ์์ฑ์ ํ์(ํ๋ผ๋ฏธํฐ๊ฐ ์๋ public ๋๋ protected ์์ฑ์)
โข
final ํด๋์ค, enum, interface, inner ํด๋์ค ์ฌ์ฉ X
โข
์ ์ฅํ ํ๋์ final ์ฌ์ฉ X
โข
name ์์ฑ์ ์ฌ์ฉํ๋ฉด JPA์์ ์ฌ์ฉํ ์ํฐํฐ ์ด๋ฆ์ ์ง์ ํ ์ ์๋ค.
[Spring][JPA]41 ์ํฐํฐ ๋งคํ, ํ๋ ์ปฌ๋ผ ๋งคํ, ๊ธฐ๋ณธํค ๋งคํ ๋ฑ ํ์ฌ๊น์ง ์ฃผ์ ์ด๋
ธํ
์ด์
์ ์ ์ ์ญํ ๊ตฌ๋ถ์ ๋ฐ๋ผ(๊ด๋ฆฌ์/์ผ๋ฐํ์/๋ฑ๋ฑ) ์ ๊ทผ ๊ฐ๋ฅํ ํ์ด์ง๋ฅผ ๊ตฌ๋ถํ๋ ๋ฐฉ๋ฒ
Controller์ ์์ฒญ๋ฉ์๋์์ Security์ @Secure ์ด๋
ธํ
์ด์
์ ํตํด์ ํ์์ ์ญํ ์ด๋ ํ์์ ๊ตฌ๋ถ์ ์ํด ๋ง๋ ํ์
์ ๋ํ์ฌ ํน์ ํ์ด์ง๋ค์ ์ ๊ทผ์ ํ์ฉ ๋๋ ๋นํ์ฉ ํ ์ ์๋ค.
ํนํ ๊ด๋ฆฌ์ ํ์ด์ง๋ ๊ด๋ฆฌ์ ์ญํ ์ ํ์๋ง์ด ๋ก๊ทธ์ธํ ์ํ์์ ์ ๊ทผํด์ผ ๋๋ ์ ์ด ์ค์ํ๊ณ , ๊ทธ ์ธ์๋ ํ์๋ค์ ์คํ์, ์ ํ์ ๋ฑ์ผ๋ก ๋ฑ๊ธ๋ณ ์ ๊ทผ ๊ฐ๋ฅ ๊ฒฝ๋ก๋ฅผ ์ธ๋ถ์ ์ผ๋ก ๋๋ ์ผ ํ ์ ์๊ธฐ ๋๋ฌธ์ ์ด ๊ธฐ๋ฅ์ ์ ํ์ฉํด์ผ ํ๋ค.
์ ๊ทผ ์์ฒญ ๋ฉ์๋์ @Secured ์ด๋
ธํ
์ด์
์ถ๊ฐ
์๋์ ๊ฐ์ด ์ ๊ทผ์ ๋ํ ์ ํ์ ๊ฑธ์ด ๋ ํ์๊ฐ์๋ ์์ฒญ์ ๋ฉ์๋์ ์ด๋
ธํ
์ด์
์ ์ถ๊ฐํ๋ค.
@Secured(UserRoleEnum.Authority.ADMIN) // <- ๊ด๋ฆฌ์ ์ ์ฉ ์ ๊ทผ๊ฐ๋ฅ ํ์ด์ง ์ค์ ๋ฐฉ๋ฒ
@GetMapping("/products/secured") // http://localhost:8080/api/products/secured -> ๊ด๋ฆฌ์ (ROLE์ด ADMIN์๋๋ฉด ์ ์์ด ๋ถ๊ฐ๋ฅํ๋ค.)
public String getProductsByAdmin(@AuthenticationPrincipal UserDetailsImpl userDetails) {
System.out.println("userDetails.getUsername() = " + userDetails.getUsername());
for (GrantedAuthority authority : userDetails.getAuthorities()) {
System.out.println("authority.getAuthority() = " + authority.getAuthority());
}
return "redirect:/";
}
์ด์ ๊ฐ์ด ์ค์ ํ๋ฉด ๋ก๊ทธ์ธ๋ ํ์์ ์ธ์ฆ ์ ๋ณด๋ก๋ถํฐ ์ฌ์ฉ์ ๊ตฌ๋ถ์ ์ํด role์ด๋ผ๋ ๊ฒ์ผ๋ก ๊ตฌ๋ถํ๋ ๊ฐ์ ์ฝ์ด์ค๋ฉฐ, ๊ณง ํด๋น ๊ฒฝ๋ก๋ ๋ก๊ทธ์ธ๋ ํ์์ role ๊ฐ์ด ADMIN์ธ ๊ฒฝ์ฐ์๋ง ํด๋น ๋ฉ์๋๋ฅผ ์คํํ๊ฒ ๋๋ค.
[Spring]43 Spring Security - @Secured ๋ฅผ ํตํ ์ ์ ๊ตฌ๋ถ์ ๋ฐ๋ฅธ ์ ๊ทผ ๊ฐ๋ฅ ํ์ด์ง, ๋๋ ๊ทธ ์ธ ์์ฒญ ์ ํํ๊ธฐ
1. Query String์ด๋
Query String์ด๋ ์๋ฒ์๊ฒ ๋ฌด์์ด ํ์ํ์ง, ํน์ ํด๋ผ์ด์ธํธ์๊ฒ ๋ฌด์์ด ํ์ํ์ง ๋ฌป๋ ๋ฌธ์์ด์ ์๋ฏธ, Query String์ ์๋์ ๊ฐ์ ํํ๋ก ๊ตฌ์ฑ๋๋ค.
# id๊ฐ 444์ธ ๊ฒ์๊ธ์ ๊ฐ์ ธ์ต๋๋ค.
/board?id=444
์์ ์์์ ๋ํ๋๋ฏ์ด ์ฃผ์ ๋ค์ โ?โ๋ฅผ ๋ถ์ฌ์ ํํํ๋ค.โkey=valueโํํ๋ก ํค์ ๊ฐ์ ์์ฑํ๊ณ ์ฌ๋ฌ ๊ฐ๋ฅผ ๋ณด๋ด๊ธฐ ์ํด โ&โ๋ฅผ ๋ถ์ฌ์ค๋ค.
์ฟผ๋ฆฌ ํ๋ผ๋ฏธํฐ๋ผ ๋ถ๋ฆฌ๋ ๊ฒ์ ์ด ์ฟผ๋ฆฌ์คํธ๋ง๊ณผ ํผ์ฉ๋์ด ์ฌ์ฉ๋๋ ๊ฒ์ผ๋ก ๊ฐ์ ์๋ฏธ๋ค.
2. Path Variable์ด๋
Path Variable์ด๋ Query String๊ณผ ๊ฐ์ด ๋ฐ์ดํฐ๋ฅผ ๋๊ธฐ๋ ๋ฐฉ๋ฒ ์ค์ ํ๋๋ก ๊ฒฝ๋ก๋ฅผ ๋ณ์์ฒ๋ผ ์ฌ์ฉํ๋ ๊ฒ์ ์๋ฏธํ๋ค. Path Variable์ ์๋์ ๊ฐ์ ํํ๋ก ๊ตฌ์ฑ๋๋ค.
[Spring]46 QueryParameter, QueryString, PathVariable์ ๊ฐ๊ฐ ๋ฌด์์ด๊ณ ์ธ์ ์ฌ์ฉ๋๋๊ฐ?
Validation์ด๋?
ํ๋ก๊ทธ๋๋ฐ์ ํ๋๋ฐ์ ์์ด์ ๊ฐ์ฅ ์ค์ํ ๋ถ๋ถ ์ค ํ๋
โข
ํนํ๋ Java๋ null ๊ฐ์ ๋ํ ์ ๊ทผ์ ๋ํด NullPointerException ์ค๋ฅ๊ฐ ๋ฐํํ๊ธฐ ๋๋ฌธ์ ์ด๋ฌํ ๋ถ๋ถ์ ์๋ฐฉํ๊ธฐ ์ํด Validation ์ฆ, ๊ฒ์ฆ ๊ณผ์ ์ด ํ์
โข
Spring์์๋ null ํ์ธ ๋ฟ ์๋๋ผ ๋ฌธ์์ ๊ธธ์ด ์ธก์ ๊ณผ ๊ฐ์ ๋ค๋ฅธ ๊ฒ์ฆ ๊ณผ์ ๋ ์ฝ๊ฒ ์ฒ๋ฆฌํ ์ ์๋๋ก Bean Validation ์ ๊ณต
โข
build.gradle : validation ์์กด์ฑ ์ฃผ์
implementation 'org.springframework.boot:spring-boot-starter-validation'
Bean Validation
โข
Bean Validation
[Spring]44 SpringBoot์์์ Validation ์ ํจ์ฑ ๊ฒ์ฌ์ ๋ก๊ทธ
์ ํจ์ฑ ๊ฒ์ฌ์ ์ปค์คํฐ๋ง์ด์ฆ
๊ธฐ๋ณธ์ ์ผ๋ก ์ ๊ณต๋๋ ์ด๋
ธํ
์ด์
ํํ์ ์ ํจ์ฑ ๊ฒ์ฌ๋ก๋ ๊ธฐ๋ณธ์ ์ธ ์ ๊ท์ ํจํด์ด ์
๋ ฅ๋์ด ์๋ค. ํ์ง๋ง ๊ฐ๋ฐ์๊ฐ ์ํ๋ ์ ๊ท ํํ์ ํจํด์ ๊ตฌํํ๋ ค๋ฉด ์ ๊ท ํํ์์ ์ดํดํ๊ณ ์์ ๋ณผ ์ ์์ด์ผ ํ๋ค.
์ ๊ท ํํ์(Regular Expression)์ด๋?
์ ๊ท์(Regular Expression)์ ๋ฌธ์์ด์์ ํน์ ๋ด์ฉ์ ์ฐพ๊ฑฐ๋ ๋์ฒด ๋๋ ๋ฐ์ทํ๋๋ฐ ์ฌ์ฉ๋๋ ๊ฒ์ด๋ค.
๊ทธ ์ค ์ ํ๋ฆฌ์ผ์ด์
์์ ์์ฃผ ํ์ฉ๋๋ ๊ฒ์ ์ ๊ท์์ ํน์ ๋ฌธ์, ์ซ์์ ๋งค์นญ ํจํด, ๊ฒ์ ๊ธฐ์ค ํจํด, ๊ฐฏ์ ๋ฐ๋ณต ํจํด ์ ๋๋ ์ต์ํด์ ธ์ผ ํ๋ค. ์ธ์ฐ์ง ๋ชปํ๋๋ผ๋ ๋ค๋ฅธ ์์ ๋ณด๊ณ ์ด๋์ ๋ ํ์
ํ ์์ค์ ์ต์ํด์ง๋ฉด ์๊ฐ์ ๋ญ๋นํ์ง ์์ ์ ์๋ค.
Spring์์๋ @Pattern ์ด๋ผ๋ ์ด๋
ธํ
์ด์
์ ํตํด์ ํด๋น ํ๋์ ์ ํจ์ฑ ๊ฒ์ฌ์ ์ ๊ท ํํ์์ ์ ์ฉ ์ํฌ ์ ์๋ค.
์์๋ก Email ํ์ : ๊ณ์ @๋๋ฉ์ธ.์ต์์๋๋ฉ์ธ ์ฒ๋ผ ๋ํ๋ด๊ณ ์ถ๋ค๋ฉด, ๋ค์๊ณผ ๊ฐ์ด ์ปค์คํฐ๋ง์ด์ฆ ํ์ฌ ๋ณด๋ค ์ธ๋ฐํ ์กฐ์ ์ ํ ์ ์๋ค.
@Pattern(regexp = "^[a-zA-Z0-9_!#$%&'*+/=?`{|}~^.-]+@[a-zA-Z0-9.-]+$")
@NotBlank
private String email;
[Spring]45 Validation์ ์ปค์คํฐ๋ง์ด์ฆ, ์ ๊ท ํํ์(Regular Expression)
[Spring][JPA]39 MySql - Java ๋ฐ์ดํฐ ํ์
1XX Informational
1xx ๋ฒ๋์ ์ํ ์ฝ๋๋ค์ ์์ฒญ์ด ์์ ๋์ด ์ฒ๋ฆฌ ์ค์ด๋ผ๋ ์๋ฏธ๋ฅผ ๊ฐ์ง๋ค.
๋ค๋ง ํ์
์์๋ ์ ์ฌ์ฉ๋์ง ์๋ ์ํ์ฝ๋์ด๊ธฐ ๋๋ฌธ์ ๊น๊ฒ ๋ค๋ฃจ์ด ์ง์ง๋ ์๋๋ค.
2XX Success
2xx ๋ฒ๋์ ์ํ ์ฝ๋๋ค์ ์์ฒญ์ด ์ ์์ ์ผ๋ก ์ฒ๋ฆฌ๋์๋ค๋ ์๋ฏธ๋ฅผ ๊ฐ์ง๋ค.
๋จ์ํ ์์ฒญ์ ๋ํ ์ฑ๊ณต์ ๋ํ๋ด์ง๋ง, ํด๋ผ์ด์ธํธ๊ฐ ์ด๋ ํ ํ์์ ๋ํ ์ฑ๊ณต์ธ์ง์ ๋ํ ๊ฒ์ ๋ํ๋ด๊ธฐ ๋๋ฌธ์, ์๋ต์ ๋ฐ๊ณ ํด๋ผ์ด์ธํธ๊ฐ ์ทจํ ํ์๋ฅผ ๊ฒฐ์ ํ๋๋ฐ ์ค์ํ๋ฉด์๋ ์ ๋ง ์์ฃผ ๋ณด๊ฒ๋ ์ํ ์ฝ๋์ผ ๊ฒ์ด๋ค.
3XX Redirection
3xx ๋ฒ๋์ ์ํ ์ฝ๋๋ค์ ๋ฆฌ๋ค์ด๋ ์
์ ์๋ฏธํ๋ฉฐ, ์ด๋ ์์ฒญ์ ์๋ฃํ๋ ค๋ฉด ์ถ๊ฐ์ ์ธ ์์
์ด ํ์ํจ์ ์๋ฏธํ๋ค.
[Spring]38 HTTP ์ํ์ฝ๋์ ๋ํ ์ ๋ฆฌ
Controller์ Service ๊ฐ์ ์์ธ ์ฒ๋ฆฌ ๊ณ์ธต ์ ํ ๊ถ์ฅ์ฌํญ
โข
Controller์์ ์์ธ ์ฒ๋ฆฌ: ์ผ๋ฐ์ ์ผ๋ก ํด๋ผ์ด์ธํธ์ ์ง์ ์ํธ์์ฉํ๋ Controller ๊ณ์ธต์์๋ ์์ธ๋ฅผ ์ ์ ํ ์ฒ๋ฆฌํ๋ ๊ฒ์ด ์ข๋ค.
โข
Controller๋ HTTP ์์ฒญ์ ๋ฐ์๋ค์ด๊ณ ์๋ต์ ๋ฐํํ๋ ์ญํ ์ ํฉ๋๋ค. ๋ฐ๋ผ์ ํด๋ผ์ด์ธํธ์๊ฒ ์ ํํ HTTP ์ํ ์ฝ๋ ๋ฐ ์๋ต ๋ฉ์์ง๋ฅผ ์ ๊ณตํ๋ ๊ฒ์ด ์ค์
โข
Controller์์ ์์ธ ์ฒ๋ฆฌ๋ฅผ ํ๋ฉด ํด๋ผ์ด์ธํธ์์ ์ํธ์์ฉ์ ๋ ๋ช
ํํ๊ฒ ๊ด๋ฆฌ
Service์์ ๋น์ฆ๋์ค ๋ก์ง ์์ธ ์ฒ๋ฆฌ
โข
๋น์ฆ๋์ค ๋ก์ง์ ์ฒ๋ฆฌํ๋ Service ๊ณ์ธต์์๋ ๋น์ฆ๋์ค ๊ท์น์ ๋ฐ๋ฅธ ์์ธ๋ฅผ ์ฒ๋ฆฌํ๋ ๊ฒ์ด ์ข๋ค.
โข
Service ๊ณ์ธต์ ๋ฐ์ดํฐ ์กฐ์ ๋ฐ ๋น์ฆ๋์ค ๊ท์น์ ๊ตฌํํ๋ ๊ณณ์ด๋ฉฐ, ๋ฐ์ดํฐ์ ์ผ๊ด์ฑ๊ณผ ์ ํจ์ฑ์ ๊ด๋ฆฌํด์ผ ํ๋ค.
โข
๋ฐ๋ผ์ ๋ฐ์ดํฐ ์กฐ์๊ณผ ๊ด๋ จ๋ ์์ธ๋ Service์์ ์ฒ๋ฆฌํ์ฌ ๋ฐ์ดํฐ์ ๋ฌด๊ฒฐ์ฑ์ ์ ์งํ ์ ์๋ค.
[Spring]40 Controller-Service ์์ธ์ฒ๋ฆฌ์ ๊ณ์ธต ์ ํ ๊ถ์ฅ์ฌํญ๊ณผ ๊ทธ ์ด์
์ ์ฝ ์กฐ๊ฑด(Constraint)
์ ์ฝ ์กฐ๊ฑด(constraint)์ด๋ ๋ฐ์ดํฐ์ ๋ฌด๊ฒฐ์ฑ์ ์งํค๊ธฐ ์ํด, ๋ฐ์ดํฐ๋ฅผ ์
๋ ฅ๋ฐ์ ๋ ์คํ๋๋ ๊ฒ์ฌ ๊ท์น์ ์๋ฏธํ๋ค.
์ด๋ฌํ ์ ์ฝ ์กฐ๊ฑด์ CREATE ๋ฌธ์ผ๋ก ํ
์ด๋ธ์ ์์ฑํ ๋๋ ALTER ๋ฌธ์ผ๋ก ํ๋๋ฅผ ์ถ๊ฐํ ๋๋ ์ค์ ํ ์๋ ์๋ค.
NOT NULL
โข
NULL ๋นํ์ฉ
โข
์ค๋ณต๊ฐ ํ์ฉ์ปฌ๋ผ์ ํ์ ํ๋ํ ์ํฌ ๋ ์ฌ์ฉ.NOT NULL ์ ์ฝ์กฐ๊ฑด ์ค์ ์ ํด๋น ์ปฌ๋ผ์๋ ๊ผญ ๋ฐ์ดํฐ๋ฅผ ์
๋ ฅํด์ผ ํจ.
โข
NOT NULL ์ ์ฝ ์กฐ๊ฑด์ CREATE ๋ฌธ์ผ๋ก ํ
์ด๋ธ์ ์์ฑํ ๋๋, ๋์ค์ ALTER ๋ฌธ์ผ๋ก ์ถ๊ฐํ ์๋ ์๋ค.
[Spring][JPA]37 ์ ์ฝ์กฐ๊ฑด(Constraint)์ ๋ํ ์ ๋ฆฌ
Spring Security Framework?
'Spring Security' ํ๋ ์์ํฌ๋ Spring ์๋ฒ์ ํ์ํ ์ธ์ฆ ๋ฐ ์ธ๊ฐ๋ฅผ ์ํด ๋ง์ ๊ธฐ๋ฅ์ ์ ๊ณตํด ์ค๋ค.
๋น์ฐํ ํ๋ ์์ํฌ๋ก์จ์ ๊ธฐ๋ฅ ๊ตฌํ์ ํธ๋ฆฌํ๊ฒ ํด์ฃผ๋ ๊ฒ์ด ๋ชฉํ์ง๋ง, ๊ทธ ๊ธฐ๋ฅ๋ค์ด ์๋น์ค์ ์ธ์ฆ/์ธ๊ฐ ๋ถ๋ถ, ๊ณง ๋ก๊ทธ์ธ, ํ์ ๊ฐ์
๋ฑ์ ์ง์ค ๋์ด ์๋ ํ๋ ์์ํฌ๋ค. ๋์ ๋ฐฉ์์ ๋์ผํ๊ฒ Bean์ผ๋ก ๋ฑ๋ก๋๋ ์ฌ๋ฌ ๊ธฐ๋ฅ๋ค์ ์์ฝ๊ฒ ์ด๋
ธํ
์ด์
์ด๋ ์ค๋ฒ๋ผ์ด๋ฉ์ ํตํด์ ๊ตฌํ ํ๊ฒ ๋๋ค.
์ค์ ๋ก ํด๋น ๋ถ๋ถ์ ์๊ฐ๋ณด๋ค ๋ง์ ๊ฐ๋ฐ์ ์๊ณ ๋ฅผ ๋์ด ์ค๋ค. ๋ง์น 'Spring' ํ๋ ์์ํฌ๊ฐ ์น ์๋ฒ ์ ๋ฐ์ ์ธ ๊ตฌํ์ ํธ์๋ฅผ ์ ๊ณตํด ์ฃผ๋ ๊ฒ๊ณผ ๋ง์ฐฌ๊ฐ์ง๋ก ๋ณด์๊ณผ ๋ฐ์ ํ ์ธ์ฆ/์ธ๊ฐ ๋ถ๋ถ์ ์ฒ๋ฆฌํด ์ฃผ๋ฉด์ ๊ฐ๋ฐ์์ ์ค์, ๋ก์ง์ ๋ณต์ก์ฑ ๋ฑ์ ๊ฐํธํ๊ฒ ์ฒ๋ฆฌํด์ฃผ๊ณ ์ค์ ๋น์ง๋์ค ๋ก์ง ๊ฐ๋ฐ์ ๋ ์ง์ค ํ ์ ์๋ ํ๊ฒฝ์ ์ ๊ณตํ๋ค.
[Spring]36_1
Spring Security ํ๋ ์์ํฌ๋? [์ถ๊ฐ์ ๋ฆฌํ์]
Spring Security ํ๋ ์์ํฌ๋? [์ถ๊ฐ์ ๋ฆฌํ์]
[Spring]36_2
Spring Security with JWT ๋ก๊ทธ์ธ ๋ฐฉ์ ๊ตฌํ [์ถ๊ฐ์ ๋ฆฌํ์]
Spring Security with JWT ๋ก๊ทธ์ธ ๋ฐฉ์ ๊ตฌํ [์ถ๊ฐ์ ๋ฆฌํ์]Load more


