반응형

JPA

https://jsonobject.tistory.com/605

 

Jackson 라이브러리는 기본적으로 엔티티의 Getter 메서드를 사용하여 JSON을 생성합니다.

따라서 Getter 메서드가 있고, 그 메서드가 연관 엔티티에 대한 접근을 포함하고 있다면,

Jackson은 해당 메서드를 호출하게 됩니다.

 

FetchType.LAZY로 설정된 연관 엔티티가 있을 경우,

Jackson이 해당 연관 엔티티의 Getter 메서드를 호출하면, JPA는 연관된 엔티티를 로드하려고 시도합니다.

만약 이 시점에서 JPA 세션이 닫혀 있으면 **LazyInitializationException**이 발생하게 됩니다.

 

해결방법

→ 연관엔티티가 있다면 Jackson 라이브러리가 Getter를 동작시키기 때문에 DTO를 사용해줘야 함

 

Jackson의 @JsonIgnore 어노테이션 사용: LAZY로 설정된 연관 엔티티의 Getter 메서드에

@JsonIgnore 어노테이션을 추가하여 Jackson이 해당 메서드를 호출하는 것을 방지합니다.

근데 결국, 연관 엔티티를 보내야 되는 경우가 있을 것이고, 그냥 DTO를 사용하는 방법이 제일 좋다.

반응형
반응형

React

렌더링 방식

  1. 내부 상태값(state)이나 중앙 상태값(redux store 등)이 변경되는 경우
  2. 부모 컴포넌트가 재렌더링되는 경우, 자식 컴포넌트도 재렌더링.

React 컴포넌트에서 return nul*을 사용하면 해당 컴포넌트는 아무것도 렌더링되지 않습니다.
이 방식을 사용하면 조건에 따라 특정 컴포넌트를 출력하지 않도록 만들 수 있습니다.

useEffect(() => {
        // 데이터를 비동기적으로 가져옵니다.
        fetchDataFromAPI().then(responseData => {
            setData(responseData);
        });
    }, []);

    // 데이터가 없을 경우 아무것도 렌더링하지 않습니다.
    if (!data) {
        return null;
    }

 

렌더링 순서

  1. 컴포넌트 함수 호출
  2. 컴포넌트 본문 호출
  3. useEffect

→ 컴포넌트 함수가 가장 먼저 호출되므로 if(!data) return loading 같은 걸로 처리

if (!data) {
    return <div>Loading...</div>;
}

 

useState

상태를 업데이트하는 함수(set)는 상태값을 변경하고 컴포넌트를 재렌더링합니다.

useEffect 첫째, 컴포넌트가 마운트된 직후에 코드를 실행하고, 둘째, 특정 상태가 변경될 때 코드를 실행합니다.

useState → 컴포넌트 렌더링 → useEffect

 

 


 

useEffect

 

  • 의존성 배열이 없는 경우 (useEffect에 두 번째 매개 변수가 전혀 주어지지 않은 경우),
    useEffect 내의 코드는 컴포넌트가 렌더링 될 때마다 실행됩니다. (무조건 실행)
  • 빈 의존성 배열이 있는 경우 (useEffect(fn, [])), useEffect 내의 코드는 컴포넌트가 처음 마운트될 때
    한 번만 실행되고, 그 후에는 실행되지 않습니다. (딱 한번)
  • 의존성 배열이 있는 경우 (useEffect(fn, [dep1, dep2, ...])), useEffect 내의 코드는 컴포넌트가
    처음 마운트될 때와 의존성 배열의 어떤 값이 변경될 때마다 실행됩니다. (감지 대상)
import React, { useState, useEffect } from 'react';

function Example() {
  const [count, setCount] = useState(0);

  useEffect(() => {
    document.title = `You clicked ${count} times`;

    // Cleanup function
    return () => {
      document.title = 'React App';
    };
  }, [count]); // 의존성 배열에 "count"를 추가합니다.

  return (
    <div>
      <p>You clicked {count} times</p>
      <button onClick={() => setCount(count + 1)}>
        Click me
      </button>
    </div>
  );
}

사용자 액션 → count useState변경 → 컴포넌트 리로딩

 

→ count 때문에 리로딩되어서 useEffect 동작

의존성 배열에 추가된 state에 의해 리로딩 된게 아니라면 useEffect가 동작하지 않는다.
의존성 배열이 없다면 첫 마운트(사이트 진입) 때에만 useEffect가 동작한다.

 

예를 들어, 서버에서 데이터를 비동기적으로 가져오는 경우에는 useEffect를 사용하여 데이터를 가져올 수 있습니다. 이 데이터를 컴포넌트의 상태에 저장하려면 useState를 사용해야 합니다.
그러면 상태가 업데이트되고 컴포넌트가 재렌더링되므로, 사용자는 최신의 데이터를 볼 수 있게 됩니다.

 

 

useEffect는 다음과 같은 다양한 경우에 사용될 수 있습니다:

  1. 비동기 작업: 서버로부터 데이터를 가져오는 등의 비동기 작업을 수행할 때 useEffect를 사용할 수 있습니다.
  2. 이벤트 리스너 설정: window의 resize 이벤트나 키보드 keydown 이벤트 등을 감지하려면 이벤트 리스너를 설정해야 하는데, 이런 작업도 useEffect에서 수행합니다.
  3. DOM 조작: React는 일반적으로 DOM을 직접 조작하지 않고, 상태 업데이트를 통해 간접적으로 DOM을 업데이트하는 것을 선호합니다. 그러나 특정 라이브러리를 사용하거나, 특정 DOM API를 직접적으로 사용해야 하는 경우에는 useEffect를 사용하여 이를 수행할 수 있습니다.
  4. 정리(cleanup) 작업: 컴포넌트가 언마운트되거나, 의존성이 변경될 때 정리 작업을 수행해야 하는 경우에도 useEffect를 사용할 수 있습니다. 예를 들어, 컴포넌트가 언마운트되면 이벤트 리스너를 제거해야 하는데, 이런 작업은 useEffect의 반환 함수에서 수행합니다.

따라서 useEffect는 비동기 작업뿐만 아니라 여러 가지 다른 사이드 이펙트를 처리하는 데 사용됩니다. 비동기 작업은 useEffect를 사용하는 주요한 경우 중 하나일 뿐입니다.

사이드 이펙트(side effect)는 프로그래밍 용어로, 함수나 컴포넌트의 주요 기능 외에 발생하는 부수적인 효과를 의미합니다. 이는 전역 상태를 변경하거나, 외부 네트워크와 통신하거나, DOM을 직접 조작하는 것과 같은 작업을 포함할 수 있습니다.

React에서 사이드 이펙트는 아래와 같은 상황들을 가리킵니다:

  1. 네트워크 요청: 서버에 데이터를 요청하거나, 서버로 데이터를 보내는 등의 네트워크 요청이 사이드 이펙트입니다. 이러한 요청은 종종 비동기적으로 수행됩니다.
  2. 구독(subscriptions): 외부 데이터 소스를 구독하는 것도 사이드 이펙트입니다. 예를 들어, WebSocket 연결을 통해 서버에서 실시간 업데이트를 받거나, 이벤트 리스너를 설정하여 사용자 입력을 감지하는 것이 이에 해당합니다.
  3. 타이머: setTimeout이나 setInterval과 같은 타이머를 설정하는 것도 사이드 이펙트입니다.
  4. 직접적인 DOM 조작: React는 일반적으로 DOM을 직접 조작하지 않고 상태 업데이트를 통해 간접적으로 DOM을 업데이트하는 것을 선호합니다. 그러나 특정 라이브러리를 사용하거나, 특정 DOM API를 직접적으로 사용해야 하는 경우에는 DOM을 직접 조작해야 할 수 있습니다.

 


 

REDUX

Store

state → 상태(데이터)

reducer → 액션(Action)을 수신, 상태(State)를 업데이트

getState → state를 가져와서 rendering

action → 상태를 갖고있는 그냥 메시지같은거

dispatch [dispatch(action)] → action으로 state변경, pub 작업을 한다.

subscribe[useSelector] → state가 변경되면, sub되어 state를 사용하는 컴포넌트들도 갱신 (화면)

      → useSelector를 사용하면 자동 subscribe함. 그래서 안씀

state는 reducers를 통해 변하고,

reducers에 action을 전달한다. action은 변화할 값을 갖고 있으며, dispatch가 action을 사용한다.

컴포넌트쪽에서는 노출된 것은 action이며, dispatch로 action을 전달한다. 한쪽에서 dispatch를 사용하면, 해당 state를 useSelector로 구독하는 모든 컴포넌트들은 상태변화를 감지한다.

// init

import React from 'react';
import ReactDOM from 'react-dom';
import { Provider } from 'react-redux';
import App from './App';
import store from './store';

ReactDOM.render(
  <Provider store={store}>
    <App />
  </Provider>,
  document.getElementById('root')
);
  1. 액션 함수를 만든다. (액션은 update data만 갖고 있음)
  2. dispatch()로 액션 함수를 전달한다.
  3. reducer에서 state, 액션함수를 인자로 받아서 변경하고 return
  4. 화면에 rendering 된다

 

ReduxTookit

reducer, action 함수를 같이 만듬

state도 slice안에서 관리함

들어온 param은 전부 payload라는 명칭을 사용

import { createSlice } from '@reduxjs/toolkit';

const chatSlice = createSlice({
  name: 'chat',
  initialState: { messages: [] },
  reducers: {
    addMessage: (state, action) => {
      state.messages.push(action.payload);
    },
    getMessage: (state, action) => {
      return state.messages.find(msg => msg.id === action.payload);
    },
    updateMessage: (state, action) => {
      const message = state.messages.find(msg => msg.id === action.payload.id);
      if (message) message.text = action.payload.text;
    },
    deleteMessage: (state, action) => {
      const index = state.messages.findIndex(msg => msg.id === action.payload);
      if (index !== -1) state.messages.splice(index, 1);
    },
  },
});

export const { addMessage, getMessage, updateMessage, deleteMessage } = chatSlice.actions;

export default chatSlice.reducer;

 

비동기 thunk

thunk를 만들고, slice → extraReducers에서 사용

pending, fulfilled, rejected 3가지 상태가 있다.

// chatSlice.js
import { createSlice, createAsyncThunk } from '@reduxjs/toolkit';
import axios from 'axios';

// 비동기 액션을 위한 Thunk
export const fetchMessages = createAsyncThunk(
  'chat/fetchMessages',
  async () => {
    const response = await axios.get('/api/messages');
    return response.data; // 이 값이 액션의 payload로 들어갑니다.
  }
);

const chatSlice = createSlice({
  name: 'chat',
  initialState: { messages: [] },
  reducers: {
    addMessage: (state, action) => {
      state.messages.push(action.payload);
    },
  },
  extraReducers: (builder) => {
    builder.addCase(fetchMessages.pending, (state) => {
        state.status = 'loading';
      })
      .addCase(fetchMessages.fulfilled, (state, action) => {
        state.status = 'succeeded';
        state.messages = action.payload;
      })
      .addCase(fetchMessages.rejected, (state, action) => {
        state.status = 'failed';
        state.error = action.error.message;
      });
  },
});

export const { addMessage } = chatSlice.actions;

export default chatSlice.reducer;

사용

import React, { useEffect } from 'react';
import { useSelector, useDispatch } from 'react-redux';
import { fetchMessages } from './chatSlice';

function Chat() {
  const dispatch = useDispatch();
  const messages = useSelector(state => state.chat.messages);

  useEffect(() => {
    dispatch(fetchMessages());
  }, [dispatch]);

  return (
    <div>
      {messages.map((message, index) => (
        <p key={index}>{message}</p>
      ))}
    </div>
  );
}

export default Chat;

 


설정

import { configureStore, combineReducers } from "@reduxjs/toolkit";
import webSocketSlice from "redux/webSocketSlice";
import chatSlice from "redux/chatSlice";
import chatRoomSlice from "redux/chatRoomSlice";

// rootReducer 생성
const rootReducer = combineReducers({
  webSocket: webSocketSlice.reducer,
  chat: chatSlice.reducer,
  chatRoom: chatRoomSlice.reducer,
});

export const store = configureStore({
  reducer: rootReducer,
});

보통 reducer만 내보낸다고함.

비동기 요청 createAsynThunk는 같은 slice 파일 내에 위치시키고

따로 export해서 사용하는 것 같다.

extraReducers는 사용하지말고, 그냥 createAsyncThunk로 모든것을 처리하자.

반응형
반응형

 

백엔드는 spring security, 프론트엔드는 Vue로 구성해서

로그인, 로그아웃, Post 요청 처리를 하는 도중

계속해서 cookie가 전달되지 않는 문제가 있었다.

 

요청마다 withCredential을 true로 설정해주면 되었는데,

또 다른 문제는 get 요청은 동작하는데 post 요청은 동작하지 않았다.

 

 

 

전역으로 설정하니 post 요청도 해결되었다.

 


 

 

 

알고보니 axios post 메서드에는 매개인자 순서가 있다.

 

post(url, data, config) 순서로 넣어주면 동작한다.

post(url, config) 으로 넣어서 동작하지 않았던 것

 

Axios api가 컴파일 에러, 런타임 에러가 나지않고 동작하여 이걸 눈치채지 못했다.

 

 

 

 


도움이 된 곳

 

https://yamoo9.github.io/axios/guide/api.html#http-%EB%A9%94%EC%84%9C%EB%93%9C-%EB%B3%84%EC%B9%AD

 

반응형

'프로그래밍 > Vue' 카테고리의 다른 글

[Vue] Vue 구조 파악해보기  (0) 2022.10.18
반응형

 


요청 시

 

토큰은 여러 종류가 있고, 헤더에 토큰 종류를 명시한다.

Authorization : Basic xxxx    - id, pass를 base64 인코딩

Authorization : Bearer xxxxx    - 보통 JWT를 사용할 경우

 

 

 

 

 


보안 설정

 

 

 

 - 서버 설정

Access-Control-Allow-Credentials : true

로 설정하게 되면,

Access-Control-Allow-Origin

를 "*" 로 할 수 없다.

 

모든 Origin의 Credentials를 허가하는 것은 위험하기 때문

Pattern, whiteList을 이용하거나, 클라이언트 Origin을 명시해서 적어야한다.

 

 

 - 브라우저 클라이언트 설정

withCredential : true

 

 

 

 

 

 

 

 

 

 

 

 

 

 

반응형
반응형

 

 

서버에서 쿠키를 만든 후, Vue에서 응답 쿠키를 확인하려고 했지만

Vue를 사용하는 브라우저에 쿠키가 들어오지 않았다.

 

 

 

이유는 서버에서 쿠키는 HttpOnlytrue로 브라우저에게 전송한다.

HttpOnly로 설정된 쿠키는 브라우저에서 실행하는 JS로 "직접 접근"할 수가 없다.

 

 

XSS공격으로 자바스크립트를 이용한 쿠키 탈취를 할 수 있기 때문에 막아둔 것.

 - 브라우저의 저장된 쿠키를 사용자가 모르게 실행시켜서, 해커 자신의 서버로 전송할 수 없도록 한다.

 

 

쿠키에는 Secure 설정도 해줄 수 있는데,

Secure true 쿠키는 https를 이용해야만 서버로 전달해줄 수 있다.

응답으로 브라우저에 저장은 가능하다.

 

 


 

해결

 

 

{ withCredentials: true } 로 설정하면 브라우저에 쿠키가 전달된다.

서버에서도 Credentials 설정을 true로 해줘야 한다.

브라우저가 쿠키 저장, 전송 처리를 허가해준다.

 

 

withCredentials

Same Origin일 경우 Request, Response 에 쿠키가 전달되지만,

Cross Origin일 경우 쿠키가 전달되지 않는다. Cross Origin의 기본은 쿠키 전송이 false이다.

true 값을 주어 전달되도록 설정해주어야한다.

인증과 인가에 사용되는 쿠키와 토큰 등을 공유하겠다는 의미

 

추가적으로, 헤더 authorization 요청을 보낼때도 허가해줘야 한다.

 

 

 

 

처리 이후 브라우저에 쿠키가 브라우저에 저장된 것을 볼 수 있었다.

 

 

 

 

 

 

 

 

 


 

 

 

 

 

 

반응형

'프로그래밍 > Security' 카테고리의 다른 글

웹 요청 정리  (0) 2022.10.20
[Web 보안] XSS, CSRF  (0) 2022.08.25
[Spring Security] 로그인 구현  (0) 2022.08.24
[인증과 인가] 쿠키와 세션, JWT  (0) 2022.06.23
반응형

Vue 설치

 

npm install -g vue

npm install -g @vue/cli

 

-g global

cli 를 사용해서 환경 설정을 통일화.

 

 

Vue 프로젝트 생성

vue create 프로젝트명

 

 

Vue 프로젝트 실행

cd 프로젝트 내부 폴더로 이동

npm run serve

 

 


 

package.json 

의존 모듈 관리

 

node_modules 

적용된 모듈들을 볼 수 있다.

 


초기 설정 살펴보기

 

 

main.js

Vue를 실행하면 가장 먼저 main.js 가 실행된다.

App.vue에 index.html 에 있는 div id="app"을 마운트 시킨다.

 

전역 설정은 이곳에 추가한다.

 


 

App.vue

라우터가 포함되어있다.

 

전역으로 사용되는 template, style을 이곳에서 관리한다.

 

 


 

 

#app은 최상위 div가 된다.

 

 


ROUTER

 

SPA는 라우터를 통해 single page로 하위 부분만 교체하므로, 사용자 경험을 향상시킨다.

 

 

<router-view/> 부분이 교체된다.

 

 

 

 

router/index.js 에서 라우터 설정을 해줄 수 있다.

path경로와 view.vue 를 매핑

 

import를 상단에 할 경우, 바로 loading,

component에서 import할 경우, 해당 path에 접근해야 import되는 lazy loading 방식으로 동작한다.

 

 

 


VIEWS

 

UI

 

화면에 보일 라우터에 의해 교체되는 template 

사용할 components를 지정해서 사용한다.

해당 view에서만 사용되는 script를 직접 작성할 수도 있다.

 

 

 


Components

 

UI + 재사용할 기능

 

화면에 보일 template + 재사용할 js  + 재사용할 css 

화면뿐만 아니라 js, css 를 여러 VIEW에서 사용할 수 있도록 component화한 것이다.

 

 

 

 

 


 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

반응형

'프로그래밍 > Vue' 카테고리의 다른 글

[Vue] Axios post cookie null  (0) 2022.10.21
반응형

 

 

 


콜백함수, Promise, async-await

 

 - 비동기의 결과를 동기적으로 처리해주기 위해서 사용한다.


 

콜백 함수

다른 함수의 인자로 들어가서 나중에 실행되는 함수

 

함수내에서 A작업이 종료 후 B작업을 진행해야 할 경우 B작업을 콜백 함수라고 한다.

A작업이 끝난 후 B작업이 실행된다.

 

콜백 함수는 단순히 작업의 순서를 결정하는 것인데, 사실 이것은 함수를 사용하지 않고도 가능하다.

콜백 함수의 유용성은 비동기 작업을 할 때 나타난다.

 

 

 

A작업이 오래걸리는 작업(서버 통신)이면 비동기적으로 처리된다.

비동기로 처리되므로 아래 코드는 바로 수행되어, 비동기 처리 결과를 받는 곳에는 빈 값이 들어온다.

그래서 이를 방지하고자 비동기 처리가 완료되면, 처리 결과 값을 받은 이후에 실행되는 함수를 따로 둔다.

그 함수가 콜백 함수(비동기 처리가 완료되면 결과를 받아주는 함수)

 

 

 

사용법

function work(CallBack){

    A작업

    let B = CallBack(A작업의 결과)

}

 

work("A를 마치고", "B가 실행")

단순하게, A의 결과를 콜백 함수의 인자로 전달한다.

비동기 처리 이후 결과의 동기적 처리를 지원한다.

 


 

Promise

비동기 작업 이후 다음 작업을 연결시키는 기능을 가진 객체

성공(resolve) 또는 실패(reject)를 리턴한다.

then()내부에 resolve, catch()내부에 reject를 콜백 함수으로 작성하여 사용한다.

 

let promise = () => {  # 프로미스 객체는 작성하면 바로 execute 되기 때문에 함수안에 두는 것이 좋다.

 return new Promise((resolve, reject) => {  

      # 비동기 작업 진행         

         resolve(성공 결과 data)         

         reject(실패 결과 error)

   }

)};

 

promise().then((response) => { })   # 성공 시

promise().catch((error) => { })    # 실패 시

 

 

 

비동기 작업 체이닝(Promise 사용 이유)

then(resolve, reject)

Promise가 작업을 성공 하면 resolve() 실행, 실패하면 reject() 실행

then은 작업을 마치면 Promise를 리턴한다.

 

then()의 반환값

https://developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Global_Objects/Promise/then

then 핸들러에서 값을 그대로 반환한 경우에는 Promise.resolve(<핸들러에서 반환한 값>)을 반환하는 것과 같다.

 

 

 

 

then 내부에 성공시 이어지는 동기 작업할 프로미스를 사용하여 체이닝을 한다.

let p1 = () => {
  return new Promise((resolve, reject)=>{
    let data = "p1 data "
    resolve(data);
  })
};

let p2 = (p1Value) => {
  return new Promise((resolve, reject) =>{
    let data = "p2 data "
    resolve(p1Value + data)
  })
};

p1()
  .then((res)=> p2(res))
  .then((res)=> console.log(res));

 

https://developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Global_Objects/Promise/then

 

프로미스를 반환할 경우, 동등한 프로미스가 메서드 체인의 그 다음 then에 노출

 


 

async-await

async-await를 사용하면 훨씬 직관적이고 편하다.

내부적으로 Promise를 사용하기 때문에 Promise를 알고 있어야 한다.

 

 

async

함수앞에 async를 붙이면 Promise 객체로 사용할 수 있다.

함수를 호출하면 Promise 객체가 반환된다.

 

 

await 

반드시 async 함수 내부에서 사용해야 한다.

await은 Promise의 완료를 기다린다.

 

      사용법

      let result1 = await fetch("url", [ options ] )             

      let result2 = await promise(result1)

      # await의 뒤는 반드시 Promise 객체여야 한다.

 

await은 then()의 대체라고 보면 된다.

결과 값을 받으며 Promise의 체이닝같은 순차 로직을 명시적으로 작성할 수 있다.

 

 

에러처리

에러처리는 async 내부에서 try{  } catch{  } 로 처리한다.

async가 붙은 함수는 Promise 이므로 asyncFunction().catch((error) => { }) 로 처리해도 된다.

 

 

 

 

 

 

 

 

 

 

 

 

 

 

반응형
반응형

public class ChanningClass {
    public final String status;

    private ChanningClass(String status){
        this.status = status;
    }

    // BuildersImpl 인스턴스를 처음 생성할 메서드가 필요하다.
    public static Builders startMethod() {
        return new BuildersImpl();
    }
    
    // Builder 인터페이스
    interface Builders{
        Builders method1(String doSomething);
        Builders method2(String doSomething);
        ChanningClass build();
    }
    
    // Builder 구현체
    private static class BuildersImpl implements Builders{
        private String status;

        BuildersImpl(){
            this.status = "";
        }

        @Override
        public Builders method1(String doSomething) {
            this.status = this.status + doSomething;
            return this;
        }

        @Override
        public Builders method2(String doSomething) {
            this.status = this.status + doSomething;
            return this;
        }
        
        // 작성한 상태값을 인스턴스를 만들며 반환
        @Override
        public ChanningClass build() {
            return new ChanningClass(this.status);
        }
    }
}

 

메서드를 시작할 때 startMethod() 내부 인스턴스를 만들고,

 

인터페이스를 이용해 메서드 체이닝을 한다.

 

메서드가 오버라이딩 되어있다면 자식 메서드가 동작한다는 점을 이용하여 동작시킨다.

 

메서드가 종료할 때 build() 외부 인스턴스를 만들어 반환한다.

 

 

 

 

 

 


ChanningClass builders1 = ChanningClass.startMethod().method1("aaaa").method2("bbbb").build();
ChanningClass builders2 = ChanningClass.startMethod().method2("bbbb").method1("aaaa").build();

System.out.println(builders1.status);
System.out.println(builders2.status);

 

 

 


@Builder

 

public static Book.BookBuilder builder() {
   return new Book.BookBuilder();
}

 

public static class BookBuilder {
   private Long id;
   private String BookName;
   private Publisher publisher;
   private List<BookLoan> loanBookList;
   private List<BookReservation> bookReservationList;

   BookBuilder() {
   }

   public Book.BookBuilder id(final Long id) {
      this.id = id;
      return this;
   }

   public Book.BookBuilder BookName(final String BookName) {
      this.BookName = BookName;
      return this;
   }

   public Book.BookBuilder publisher(final Publisher publisher) {
      this.publisher = publisher;
      return this;
   }

   @JsonIgnore
   public Book.BookBuilder loanBookList(final List<BookLoan> loanBookList) {
      this.loanBookList = loanBookList;
      return this;
   }

   @JsonIgnore
   public Book.BookBuilder bookReservationList(final List<BookReservation> bookReservationList) {
      this.bookReservationList = bookReservationList;
      return this;
   }

   public Book build() {
      return new Book(this.id, this.BookName, this.publisher, this.loanBookList, this.bookReservationList);
   }

 

@Builder는 굳이 인터페이스까진 만들지 않는다.

 

내부 인스턴스를 만드는 builder(), 외부 인스턴스를 만드는 build() 

 

시작과 끝 메서드의 사용 방식은 같다.

 

 

 

 

 

 

 

 

 

 


 

 

 

 

 

 

 

반응형
반응형

 


REST(Represetational State Transfer)

기술이 아닌 약속이다.

요청에 대한 JSON 또는 XML형식으로 자원의 상태 응답

자원(데이터)의 상태를 주고 받는 것

 

 

특징

Client, Server 구조 / 클라이언트는 요청하고, 서버는 응답하는 구조

Stateless / 서버에서는 클라이언트의 상태 값을 저장하지 않는다. (인증, 인가에 JWT 사용)

 

HTTP 프로토콜을 이용해서 JSON 데이터를 응답으로 주기 때문에 어플리케이션의 통합과 분리가 용이하다.

 

 

규약

자원의 식별 - URL로 자원을 구분, 식별

메세지를 통한 리소스 조작 - HTTP header에 자원의 타입을 명시, 데이터 일부를 메세지로 전달

자기 서술적 메세지 - 요청하는 데이터가 HTTP methods 중 무엇인지, 필요한 정보를 포함하여 전달

어플리케이션 - 데이터뿐만 아니라 추가 정보도 제공

 

 

URI 설계 패턴

URI - 특정 자원에 대한 URI주소로 접근. 변경될 수 있다.

URL - 특정 자원에 대한 위치. 변경될 수 없다.

 

슬래시로 계층을 구분

하이픈을 사용한다.

소문자가 적합

컬렉션은 복수형을 사용

 

CRUD 명시 하지 않는다.

파일형식, 경로를 명시하지 않는다.

 

 

HTTP Methods

클라이언트가 요청을하고 서버의 응답을 기다린다.

 

GET

자원의 취득

데이터 기준: 안정성 o, 멱등성 o (데이터가 변하지 않는다.)

body를 사용하지 않는다.

 

POST

자원의 생성

데이터 기준: 안전성 x, 멱등성 x (매번 데이터가 생성)

body를 사용한다.

 

PUT

자원의 수정

데이터 기준: 안전성 x, 멱등성 o (한번 수정되고 같은 결과)

body를 사용한다.

 

DELETE

자원의 삭제

데이터 기준: 안전성 x, 멱등성 o (한번 삭제되고 같은 결과)

body를 사용하지 않는다.

 

 

HTTP Status Code

100 - 처리가 계속 되고 있는 상태

200 - 성공

300 - 리다이렉션

400 - 클라이언트 에러

500 - 서버 에러

 

 

 


Java  -- Json

 

 

object-> String(json형태)

ObjectMapper writeValueAsString()

 

 

String(json형태) -> object

ObjectMapper readValue()

 

 

String(json형태) -> json

ObjectMapper readTree()

 

 

JsonNode  값을 변경할 수 없다.ObjectNode  값을 변경할 수 있다.

 

 


 

Jackson

package com.example.jacksontest.dto;

import lombok.*;

import java.util.List;

@Getter
@Setter
@ToString
@NoArgsConstructor
@AllArgsConstructor
public class User {
    private int id;
    private String name;
    private List<Item> items;

    @Getter
    @Setter
    @NoArgsConstructor
    @ToString
    @AllArgsConstructor
    public static class Item{
        private String itemName;
        private int count;

    }

}

 

 

 

{
  "id" : 1,
  "name" : "cha",
  "items" : [
    {
      "itemName" : "coin",
      "count" : 3
    },{
      "itemName" : "Phone",
      "count" : 1
    },{
      "itemName" : "cigarette",
      "count" : 5
    }
  ]

}

 

 

 

ObjectMapper objectMapper = new ObjectMapper();
// 읽기
User user = objectMapper.readValue(new File("src/main/resources/data.json"), User.class);
System.out.println(user);

// 쓰기
File f = new File("src/main/resources/data1.json");
objectMapper.writeValue(f, user);

// Json -> String 으로 변환
String s = objectMapper.writeValueAsString(user);

// String -> ObjectNode 로 변환
ObjectNode objectNode = (ObjectNode) objectMapper.readTree(s);
System.out.println(objectNode.toPrettyString());

// Json -> Java Pojo
JsonNode list = objectNode.get("items");
User user1 = objectMapper.readValue(s, new TypeReference<User>() {});
System.out.println(user1);

// key 를 이용해 하나씩 접근
System.out.println("key 로 접근: " + objectNode.get("name"));
System.out.println("Array 일 경우: " + objectNode.get("items"));

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

반응형
반응형

 

 

XSS(Cross-Site Scripting)

(사용자가 사이트를 신뢰한다는 점을 이용)

스크립트 삽입 공격 방식

 

JS 스크립트는 어느위치에 있던 동작하기 때문에 공격자가 스크립트 코드가 포함된 게시글을 작성하여 등록

일반 사용자가 해당 글을 클릭하면 script가 동작하여 이루어지는 공격 방식

 

무조건 Secure Coding을 해줘야 한다. 

 

 

대응

 - Secure coding

<, >, = 과 같은 입력값에 공격에 사용될만한 문자가 있다면 치환하여 사용, DB에 저장한다.

Sql Injection도 비슷한 방식으로 대응할 수 있다.

 

 - HttpOnly

Secure coding이 적용되어 있지 않아 스크립트가 동작

쿠키 탈취 시도 시 쿠키가 HttpOnly 설정이 되어있다면 js로 쿠키를 읽어낼 수 없다.

 

 


 

CSRF (Cross Site Request Forgery)

(사이트가 사용자를 신뢰한다는 점을 이용, 신뢰할만한 쿠키와 토큰을 브라우저에서 갖고 있다는 점)

피싱 사이트를 만들어, 피싱 사이트에 사용자가 접근하면

스크립트를 이용해 특정 사이트로 요청을 보내, 사용자나 관리자의 권한으로 서버에 write 하도록 유도하는 공격

피싱 사이트에서 요청을 위조해서 Request Forgery

 

기본적으로 브라우저의 SOP 정책으로 응답된 데이터에 접근이 불가능하다. (공격자가 해당 정보를 이용할 수 없다)

하지만, 응답된 데이터 접근 불가능할뿐이지 서버에 write는 가능하다. (공격자가 원하는 대로 정보를 수정)

그래서 write를 방지하고자 Preflight 또는 CSRF token을 도입하게 되었다.

 

 

대응

 - Preflight

(1) 예비 요청을 Option 메서드로 보내서, 서버의 Access-Control-Allow-Origin 을 확인한다.

서버에서 허가된 Access-Control-Allow-Origin 목록을 응답으로 보내준다.

 

(2-1) 허가된 주소에 해당되면 정식 요청을 보내고, 응답을 사용한다.

(2-2) 허가된 Origin이 아니라면 예비 요청에서 막혔음을 확인했기 때문에 정식 요청을 보내지 않는다.

get, head, post 가 아닌 서버 데이터를 변형시키는 put, patch, delete면 Prefilght를 보내게 된다.

 

 - CSRF Token

CSRF토큰 난수를 두면 요청시에 모든 요청은 CSRF토큰 난수를 보내야 인가를 받는다.

서버에서 CSRF Token 난수가 맞는지 확인한다. 서버에서 만든 페이지에서 요청한 것이 맞으므로 수행

위조된 요청은 CSRF Token가 없으므로 write가 동작하지 않는다.

 

 

 


브라우저의 보안 SOP

SOP(Same Origin Policy)

브라우저가 적용한 보안 방식

요청한 Origin과 응답한 Origin이 Same Origin이어야 자원 접근이 가능하다.

script뿐만 아니라, document도 출신(Origin)이 다르면 서로 접근이 불가능하다.

 

 

XSS는 Secure Coding으로 막았다고 가정, CSRF 공격 시

해킹1. (document로 정보 가져오기)

요청한 위조 사이트와 정보를 가져온 사이트의 Origin이 다르기 때문에,

유저 인증을 이용해 document를 가져오는 건 성공해도 document를 읽어낼 수 없다.

결과적으로 해커의 서버에 전송된 document에는 아무것도 없다

 

해킹2. (script로 정보 가져오기)

요청하는 해커의 서버와 응답 사이트의 Origin이 다르기 때문에 요청이 무시되어 동작하지 않는다.

 

 

 

CORS(Cross Origin Resource Sharing)

SOP 보안 정책을 일부 허가된 Origin은 해제

script, document로 다른 Origin 서버에 요청해서 받은 자원을 접근할 수 있게 한다.

 

서버 Access-Control-Allow-Origin 에 요청하는 Origin을 추가한다.

 

 

 


 

HTTPS 

네트워크상에서 정보가 탈취되지 않도록 방지한다.

RSA 암호화를 사용하는 것. 서버가 비밀키를 갖고 있다.

공개키로 암호화해서 데이터를 보내고 전달받은 데이터를 서버가 자신의 비밀키로 푼다.

RSA 암호화는 컴퓨터에게 부담을 주기 때문에 이후부터는 대칭키를 교환하여 사용한다.

 

RSA

두 소수를 곱하여 큰 수를 만들어낸다. (두 소수도 자체도 크다.)

두 소수로 큰 수를 구하는 것은 쉽지만

반대로, 큰 수에서 두 소수를 구하는 것은 어렵다는 특징

반응형

+ Recent posts