Archive: 2021

0

React 로그인 Modal 만들기 9 - Redux Saga 적용

React 로그인 Modal 만들기 9 - Redux Saga 적용 React 로그인 Modal 만들기 8 - Redux 사용하기 React 로그인 Modal 만들기 7 - JWT 적용하기 React 로그인 Modal 만들기 6 - Login 구현 React 로그인 Modal 만들기 5 - 회원 가입 Back end 구현 React 로그인 Modal 만들기 4 - 비동기 처리 React 로그인 Modal 만들기 3 - 회원 가입 React 로그인 Modal 만들기 2 - styled-component 사용하기 React 로그인 Modal 만들기 React 로그인 Modal 만들기 9 - Redux Saga 적용모듈 설치하기yarn add redux-saga Reducer 정의import { SIGNUP_FAILURE, SIGNUP_REQUEST, SIGNUP_SUCCESS } from "../type";const initialState = "";export const signupRequest = (form) => ({ type: SIGNUP_REQUEST, data: form,});const SignupReducer = (state = initialState, action) => { switch (action.type) { case SIGNUP_REQUEST: return { ...state, }; case SIGNUP_SUCCESS: return { ...state, }; case SIGNUP_FAILURE: return { ...state, }; default: return state; }};export default SignupReducer; import axios from "axios";import { LOGIN_FAILURE, LOGIN_REQUEST, LOGIN_SUCCESS } from "../type";const initialState = { token: localStorage.getItem("token"),};export const loginRequest = (loginData) => ({ type: LOGIN_REQUEST, data: loginData,});const LoginReducer = (state = initialState, action) => { switch (action.type) { case LOGIN_REQUEST: console.log("Log In Reqeust"); return { ...state, }; case LOGIN_SUCCESS: console.log("Log In Success"); return { ...state, token: localStorage.getItem("token"), }; case LOGIN_FAILURE: console.log("Log In Failure"); return { ...state, }; default: return state; }};export default LoginReducer; Root Reducer 생성import { combineReducers } from "redux";import LoginReducer from "./LoginReducer";import SignupReducer from "./SignupReducer";const createRootReducer = combineReducers({ LoginReducer, SignupReducer,});export default createRootReducer;

0

Spring Security - 인가 API ExpressionUrlAuthorizationConfigurer

목차 Spring Security 권한 계층 사용하기 - @RoleHierarcy Spring Security - DelegateFilterProxy Spring Security - Remember Me와 관련된 인증 API - RememberMeConfigurer Spring Security - RembmerMeAuthenticationFilter Spring Security - SessionManagementFilter & ConcurrentSessionFilter Spring Security - 인가 API ExpressionUrlAuthorizationConfigurer Spring Security - Security 설정을 위한 WebSecurityConfigurerAdatper Spring Security - AuthenticationProvider Spring Security - AuthenticationManager Spring Security - UsernamePasswordAuthenticationFilter & AbstractAuthenticationProcessingFilter Spring Security - SecurityContextHolder 와 SecurityContext Spring Security - Authentication 객체 인가 API ExpressionUrlAuthorizationConfigurer 를 통해 자원에 접근하기 위한 다양한 인가 방식을 지원한다.역할(ROLE) 기반 인가 방식, 권한(Authority) 기반 인가 방식, IP 를 통한 인가 방식등 다양한 API를 제공한다. hasRole 명시된 권한에 접미사 ROLE_ 을 붙이고 해당 권한을 가진 사용자만 자원에 접근을 허용한다. hasAuthority 와 비슷한 개념이지만 hasRole 은 USER, MANAGER, ADMIN과 같은 역할 에 따라 자원에 대한 접근을 허용한다. hasAnyRole 명시된 권한들에 접미사 ROLE_ 을 붙이고 사용자가 하나라도 권한을 갖고 있으면 해당 자원에 대한 접근을 허용한다. hasAuthority 해당 권한을 가진 사용자만 자원에 접근을 허용한다. hasRole 와 비슷한 개념이지만 hasAuthority는 CREATE, READ, WRITE, DELETE 와 같이 권한 에 따라 자원에 대한 접근을 허용한다. hasAnyAuthority 명시된 권한들 중 하나라도 권한이 있는 사용자만 자원에 접근을 허용한다. hasIpAddress 특정 IP 에 대해 자원에 대한 접근을 허용할 때 사용한다. permitAll 권한에 대한 검증 없이 자원에 대한 접근을 허용한다. anonymous 익명 사용자의 접근을 허용 익명 사용자는 기본적으로 ROLE_ANONYMOUS 권한이 부여돼 있다. rememberMe Remember-Me 를 통해 인증된 사용자가 자원에 대한 접근을 허용한다. denyAll 모든 접근을 허용하지 않는다. authenticated 인증된 사용자만 자원에 대한 접근을 허용한다. fullyAuthenticated Remember-Me 를 통해 인증된 사용자를 제외하고 인증된 사용자에 대한 접근을 허용한다. ExpressionUrlAuthorizationConfigurerpublic ExpressionInterceptUrlRegistry hasRole(String role) { return access(ExpressionUrlAuthorizationConfigurer .hasRole(ExpressionUrlAuthorizationConfigurer.this.rolePrefix, role));}public ExpressionInterceptUrlRegistry hasAnyRole(String... roles) { return access(ExpressionUrlAuthorizationConfigurer .hasAnyRole(ExpressionUrlAuthorizationConfigurer.this.rolePrefix, roles));}public ExpressionInterceptUrlRegistry hasAuthority(String authority) { return access(ExpressionUrlAuthorizationConfigurer.hasAuthority(authority));}public ExpressionInterceptUrlRegistry hasAnyAuthority(String... authorities) { return access(ExpressionUrlAuthorizationConfigurer.hasAnyAuthority(authorities));}public ExpressionInterceptUrlRegistry hasIpAddress(String ipaddressExpression) { return access(ExpressionUrlAuthorizationConfigurer.hasIpAddress(ipaddressExpression));}public ExpressionInterceptUrlRegistry permitAll() { return access(permitAll);}public ExpressionInterceptUrlRegistry anonymous() { return access(anonymous);}public ExpressionInterceptUrlRegistry rememberMe() { return access(rememberMe);}public ExpressionInterceptUrlRegistry denyAll() { return access(denyAll);}public ExpressionInterceptUrlRegistry authenticated() { return access(authenticated);}public ExpressionInterceptUrlRegistry fullyAuthenticated() { return access(fullyAuthenticated);}public ExpressionInterceptUrlRegistry access(String attribute) { if (this.not) { attribute = "!" + attribute; } interceptUrl(this.requestMatchers, SecurityConfig.createList(attribute)); return ExpressionUrlAuthorizationConfigurer.this.REGISTRY;}

0

Spring Security - SessionManagementFilter & ConcurrentSessionFilter

목차 Spring Security 권한 계층 사용하기 - @RoleHierarcy Spring Security - DelegateFilterProxy Spring Security - Remember Me와 관련된 인증 API - RememberMeConfigurer Spring Security - RembmerMeAuthenticationFilter Spring Security - SessionManagementFilter & ConcurrentSessionFilter Spring Security - 인가 API ExpressionUrlAuthorizationConfigurer Spring Security - Security 설정을 위한 WebSecurityConfigurerAdatper Spring Security - AuthenticationProvider Spring Security - AuthenticationManager Spring Security - UsernamePasswordAuthenticationFilter & AbstractAuthenticationProcessingFilter Spring Security - SecurityContextHolder 와 SecurityContext Spring Security - Authentication 객체 SessionManagementFilter SessionManagementFilter 는 인증된 사용자에 대한 Session 관리 와 Session 공격으로 부터의 보호 를 위한 Filter Session 관리 인증시 사용자의 Session 등록, 조회, 삭제 등의 이력을 관리한다. 동시성 Session 제어 동일 계정으로 접속하는 최대 Session 수를 설정한다. Session 고정 보호 인증할때 마다 새로운 Session 쿠키를 새로 발급해 쿠키 조작을 방지한다. Session 생성 정책 SessionManagementFilter.java private void doFilter(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws IOException, ServletException { if (request.getAttribute(FILTER_APPLIED) != null) { chain.doFilter(request, response); return; } request.setAttribute(FILTER_APPLIED, Boolean.TRUE); if (!this.securityContextRepository.containsContext(request)) { Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); if (authentication != null && !this.trustResolver.isAnonymous(authentication)) { try { // 최대 생성 개수를 넘어섰는지?, Session 고정 공격이 들어왔는지? 등 // Session에 대한 인증을 진행한다. this.sessionAuthenticationStrategy.onAuthentication(authentication, request, response); } catch (SessionAuthenticationException ex) { this.logger.debug("SessionAuthenticationStrategy rejected the authentication object", ex); SecurityContextHolder.clearContext(); this.failureHandler.onAuthenticationFailure(request, response, ex); return; } this.securityContextRepository.saveContext(SecurityContextHolder.getContext(), request, response); } else { if (request.getRequestedSessionId() != null && !request.isRequestedSessionIdValid()) { if (this.logger.isDebugEnabled()) { this.logger.debug(LogMessage.format("Request requested invalid session id %s", request.getRequestedSessionId())); } if (this.invalidSessionStrategy != null) { this.invalidSessionStrategy.onInvalidSessionDetected(request, response); return; } } } } chain.doFilter(request, response);} Session 관리를 위한 SessionAuthenticationStrategy SessionAuthenticationStrategy 는 Session 이 존재하는지 확인하거나 Session 고정 공격에 대한 보호를 위해 Session Id를 변경한다.

0

Spring Security - RembmerMeAuthenticationFilter

목차 Spring Security 권한 계층 사용하기 - @RoleHierarcy Spring Security - DelegateFilterProxy Spring Security - Remember Me와 관련된 인증 API - RememberMeConfigurer Spring Security - RembmerMeAuthenticationFilter Spring Security - SessionManagementFilter & ConcurrentSessionFilter Spring Security - 인가 API ExpressionUrlAuthorizationConfigurer Spring Security - Security 설정을 위한 WebSecurityConfigurerAdatper Spring Security - AuthenticationProvider Spring Security - AuthenticationManager Spring Security - UsernamePasswordAuthenticationFilter & AbstractAuthenticationProcessingFilter Spring Security - SecurityContextHolder 와 SecurityContext Spring Security - Authentication 객체 참고 https://docs.spring.io/spring-security/reference/servlet/authentication/rememberme.html RembmerMeAuthenticationFilter Client 로부터 Request(요청) 이 오게 되면 Request 객체 Cookie 에 Remember Me Token 이 있는지 확인한다. Token 을 이용해 인증을 진행한 후 SecurityContext 에 Authentication 객체를 저장한다. RememberMeAuthenticationFilter.java private void doFilter(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws IOException, ServletException { // SecurityContext 내 Authentication 객체가 있는지 확인 한다. if (SecurityContextHolder.getContext().getAuthentication() != null) { this.logger.debug(LogMessage .of(() -> "SecurityContextHolder not populated with remember-me token, as it already contained: '" + SecurityContextHolder.getContext().getAuthentication() + "'")); chain.doFilter(request, response); return; } // Remember Me 인증을 진행한 후 Authentication 객체를 반환받는다. Authentication rememberMeAuth = this.rememberMeServices.autoLogin(request, response); if (rememberMeAuth != null) { // Attempt authenticaton via AuthenticationManager try { // RememberMeAuthenticationToken 객체내 Key 값에 대한 Hash 비교를 통해 유효성 인증을 진행 후 객체를 그대로 반환받는다. rememberMeAuth = this.authenticationManager.authenticate(rememberMeAuth); // SecurityContextHolder 에 인증 받은 Authentication 객체를 저장한다. SecurityContextHolder.getContext().setAuthentication(rememberMeAuth); // 인증 성공 후 후작업을 진행한다. onSuccessfulAuthentication(request, response, rememberMeAuth); this.logger.debug(LogMessage.of(() -> "SecurityContextHolder populated with remember-me token: '" + SecurityContextHolder.getContext().getAuthentication() + "'")); if (this.eventPublisher != null) { this.eventPublisher.publishEvent(new InteractiveAuthenticationSuccessEvent( SecurityContextHolder.getContext().getAuthentication(), this.getClass())); } if (this.successHandler != null) { this.successHandler.onAuthenticationSuccess(request, response, rememberMeAuth); return; } } catch (AuthenticationException ex) { this.logger.debug(LogMessage .format("SecurityContextHolder not populated with remember-me token, as AuthenticationManager " + "rejected Authentication returned by RememberMeServices: '%s'; " + "invalidating remember-me token", rememberMeAuth), ex); this.rememberMeServices.loginFail(request, response); onUnsuccessfulAuthentication(request, response, ex); } } chain.doFilter(request, response);} AbstractRememberMeServices

0

React 로그인 Modal 만들기 8 - Redux 사용하기

React 로그인 Modal 만들기 9 - Redux Saga 적용 React 로그인 Modal 만들기 8 - Redux 사용하기 React 로그인 Modal 만들기 7 - JWT 적용하기 React 로그인 Modal 만들기 6 - Login 구현 React 로그인 Modal 만들기 5 - 회원 가입 Back end 구현 React 로그인 Modal 만들기 4 - 비동기 처리 React 로그인 Modal 만들기 3 - 회원 가입 React 로그인 Modal 만들기 2 - styled-component 사용하기 React 로그인 Modal 만들기 React 로그인 Modal 만들기 8 - Redux 사용하기모듈 설치하기yarn add reduxyarn add react-redux Action 정의export const LOGIN_REQUEST = "login/LOGIN_REQUEST"; Action 생성 함수 및 Reducer 생성하기import axios from 'axios';import { LOGIN_REQUEST } from "../type"const initialState = { token: localStorage.getItem('token'),};export const loginRequest = (loginData) => { const response = axios.post("/api/login", loginData) .then((response) => { localStorage.setItem("token", response.headers.authorization); console.log(localStorage.getItem("token")); return response.data; }) .catch((err) => { console.log(err); }); return { type: LOGIN_REQUEST, data: response };};const LoginReducer = (state = initialState, action) => { switch (action.type) { case LOGIN_REQUEST: return { ...state, token: localStorage.getItem('token'), }; default: return state; }}export default LoginReducer; Store 생성하기

0

React 로그인 Modal 만들기 7 - JWT 적용하기

React 로그인 Modal 만들기 9 - Redux Saga 적용 React 로그인 Modal 만들기 8 - Redux 사용하기 React 로그인 Modal 만들기 7 - JWT 적용하기 React 로그인 Modal 만들기 6 - Login 구현 React 로그인 Modal 만들기 5 - 회원 가입 Back end 구현 React 로그인 Modal 만들기 4 - 비동기 처리 React 로그인 Modal 만들기 3 - 회원 가입 React 로그인 Modal 만들기 2 - styled-component 사용하기 React 로그인 Modal 만들기 React 로그인 Modal 만들기 7 - JWT 적용하기Properties에 JWT 설정 값 추가JWT 를 암호화 하기 위한 key 값, token의 유효시간을 정의하기 위한 값, 그리고 토큰을 Http Header에 저장하기 위핸 Header Key를 정의해준다. application.yml jwt: header: Authorization secret: c2lsdmVybmluZS10ZWNoLXNwcmluZy1ib290LWp3dC10dXRvcmlhbC1zZWNyZXQtc2lsdmVybmluZS10ZWNoLXNwcmluZy1ib290LWp3dC10dXRvcmlhbC1zZWNyZXQK token-validity-in-seconds: 86400000 JWT를 다루기 위한 JwtUtils 생성 JwtUtils에서는 createToken 메소드를 이용해 새로운 JWT를 만들고 validateToken 메소드를 이용해 전달 받은 JWT의 유효성을 검증하고 getAuthentication 메소드를 이용해 전달 받은 JWT로부터 Authentication 객체를 가져오도록 한다. Jwts.builder 를 이용해 JWT를 생성하고 Jwts.parser 를 이용해 JWT로부터 정보를 가져온다. Value 어노테이션을 이용해 Properties에 저장한 token을 암호화 하기 위한 Key값과 token 유효시간 정보를 가져온다.

0

React 로그인 Modal 만들기 6 - Login 구현

React 로그인 Modal 만들기 9 - Redux Saga 적용 React 로그인 Modal 만들기 8 - Redux 사용하기 React 로그인 Modal 만들기 7 - JWT 적용하기 React 로그인 Modal 만들기 6 - Login 구현 React 로그인 Modal 만들기 5 - 회원 가입 Back end 구현 React 로그인 Modal 만들기 4 - 비동기 처리 React 로그인 Modal 만들기 3 - 회원 가입 React 로그인 Modal 만들기 2 - styled-component 사용하기 React 로그인 Modal 만들기 React 로그인 Modal 만들기 6 - Login 구현Json 인증 Filter 구현하기UsernamePasswordAuthenticationFilter 의 경우 Form Login을 위해 구현된 Filter라 Request Body로 요청이 들어오는 Login 요청을 적용하기는 어렵기에 JSON 을 이용해 Login을 진행할 수 있도록 JsonAuthenticationFilter 를 새로 만들어줄 필요가 있다. UsernamePasswordAuthenticationFilter 와 인증을 진행하는 과정은 비슷하므로 AbstractAuthenticationProcessingFilter 를 상속해 새로운 Filter를 생성해준다. JsonAuthenticationFilter.java public class JsonAuthenticationFilter extends AbstractAuthenticationProcessingFilter { private static final String LOGIN_PROCESSING_URL = "/api/login"; @Autowired private ObjectMapper objectMapper; public JsonAuthenticationFilter() { super(new AntPathRequestMatcher(LOGIN_PROCESSING_URL)); } @Override public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException, IOException, ServletException { if(!isApplicationJSON(request)){ throw new IllegalStateException("Content Type is not Application/json"); } LoginDto loginDto = objectMapper.readValue(request.getReader(), LoginDto.class); if(ObjectUtils.isEmpty(loginDto.getUsername()) || ObjectUtils.isEmpty(loginDto.getPassword())){ throw new IllegalArgumentException("Username or Password is empty"); } JsonAuthenticationToken jsonAuthenticationToken = new JsonAuthenticationToken(loginDto.getUsername(), loginDto.getPassword()); getAuthenticationManager().authenticate(jsonAuthenticationToken); return null; } private boolean isApplicationJSON(HttpServletRequest httpServletRequest){ if(httpServletRequest.getHeader("Content-type").equals(MediaType.APPLICATION_JSON_VALUE)){ return true; } return false; }} 새로운 Authentication Token 만들기 JsonAuthenticationFilter 에서 사용하기 위한 새로운 Authentication 생성

0

React 로그인 Modal 만들기 5 - 회원 가입 Back end 구현

React 로그인 Modal 만들기 9 - Redux Saga 적용 React 로그인 Modal 만들기 8 - Redux 사용하기 React 로그인 Modal 만들기 7 - JWT 적용하기 React 로그인 Modal 만들기 6 - Login 구현 React 로그인 Modal 만들기 5 - 회원 가입 Back end 구현 React 로그인 Modal 만들기 4 - 비동기 처리 React 로그인 Modal 만들기 3 - 회원 가입 React 로그인 Modal 만들기 2 - styled-component 사용하기 React 로그인 Modal 만들기 React 로그인 Modal 만들기 5 - 회원 가입 Back end 구현회원 데이터를 저장할 UserInfo 정의회원 가입을 위해 사용자 id, username, email, password와 사용자 별로 권한을 관리하기 위한 UserInfo 객체를 관리하도록 정의 @Entity@NoArgsConstructor@AllArgsConstructor@Getter@Builderpublic class UserInfo { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; @Column(nullable = false) private String username; @Column(nullable = false) private String email; @Column(nullable = false) private String password; @Enumerated(EnumType.STRING) private UserRole userRole;} User 권한 목록 권한은 3가지로 일반사용자를 위한 USER 권한, 수정/변경 권한이 있는 MANAGER 권한과 관리자인 ADMIN 3가지 권한으로 분류 한다 public enum UserRole { USER("ROLE_USER"), MANAGER("ROLE_MANAGER"), ADMIN("ROLE_ADMIN"); private String value; UserRole(String value){ this.value = value; } public String getValue(){ return this.value; }} 사용자 이름과 Email을 이용해 사용자 정보를 가져올 수 있도록 메소드를 추가한다.

0

React 로그인 Modal 만들기 4 - 비동기 처리

React 로그인 Modal 만들기 9 - Redux Saga 적용 React 로그인 Modal 만들기 8 - Redux 사용하기 React 로그인 Modal 만들기 7 - JWT 적용하기 React 로그인 Modal 만들기 6 - Login 구현 React 로그인 Modal 만들기 5 - 회원 가입 Back end 구현 React 로그인 Modal 만들기 4 - 비동기 처리 React 로그인 Modal 만들기 3 - 회원 가입 React 로그인 Modal 만들기 2 - styled-component 사용하기 React 로그인 Modal 만들기 React 로그인 Modal 만들기 4 - 비동기 처리import axios from "axios";import React, { useState, useCallback } from "react";import { LoginBody, LoginFooter, LoginHeader, ModalBackGround, ModalBox,} from "../assets/css/login";const LoginTemplate = ({ open, close }) => { const [form, setForm] = useState({ email: "", password: "", }); const onChange = useCallback( (e) => { setForm({ ...form, [e.target.name]: e.target.value, }); }, [form] ); const onClick = useCallback( (e) => { axios .post("/api/login", form) .then((response) => { console.log(response); }) .catch((err) => { console.log(err); }); }, [form] ); return open ? ( <ModalBackGround> <ModalBox> <LoginHeader> 로그인 <button className="close" onClick={close}> {" "} &times;{" "} </button> </LoginHeader> <LoginBody className="login-body"> <div> <input id="email" name="email" type="email" placeholder="Email" onChange={onChange} /> </div> <div> <input id="password" name="password" type="password" placeholder="Password" onChange={onChange} /> </div> </LoginBody> <LoginFooter> <button onClick={onClick}>로그인</button> </LoginFooter> </ModalBox> </ModalBackGround> ) : null;};export default LoginTemplate; import React, { useCallback, useState } from "react";import { ModalBox } from "../assets/css/login";import axios from "axios";import { ModalBackGround, SignUpBody, SignUpFooter, SignUpHeader,} from "../assets/css/signup";const SignUpTemplate = ({ open, onChangeOpen }) => { const [form, setForm] = useState({ username: "", email: "", password: "", }); const onChange = useCallback( (e) => { setForm({ ...form, [e.target.name]: e.target.value, }); }, [form] ); const onClickSignUp = useCallback(() => { axios.post("/api/signup", form).then((Response) => { console.log(Response); }); }, [form]); return open ? ( <ModalBackGround className="signup-modal-box"> <ModalBox> <SignUpHeader> 회원 가입 <button className="close" onClick={onChangeOpen}> {" "} &times;{" "} </button> </SignUpHeader> <SignUpBody> <div> <input type="username" id="username" name="username" placeholder="이름" onChange={onChange} /> </div> <div> <input type="email" id="email" name="email" placeholder="Email" onChange={onChange} /> </div> <div> <input type="password" id="password" name="password" placeholder="Password" onChange={onChange} /> </div> </SignUpBody> <SignUpFooter> <button onClick={onClickSignUp}>회원가입</button> </SignUpFooter> </ModalBox> </ModalBackGround> ) : null;};export default SignUpTemplate;

0

React 로그인 Modal 만들기 3 - 회원 가입

React 로그인 Modal 만들기 9 - Redux Saga 적용 React 로그인 Modal 만들기 8 - Redux 사용하기 React 로그인 Modal 만들기 7 - JWT 적용하기 React 로그인 Modal 만들기 6 - Login 구현 React 로그인 Modal 만들기 5 - 회원 가입 Back end 구현 React 로그인 Modal 만들기 4 - 비동기 처리 React 로그인 Modal 만들기 3 - 회원 가입 React 로그인 Modal 만들기 2 - styled-component 사용하기 React 로그인 Modal 만들기 React 로그인 Modal 만들기 3 - 회원 가입import styled, { keyframes } from "styled-components";export const ModalFade = keyframes`{ from { opacity: 0; margin-top: -50px; } to { opacity: 1; margin-top: 0; }}`;export const ModalBackGround = styled.div` position: fixed; top: 0; left: 0; width: 100%; height: 100%; background: rgba(0, 0, 0, 0.5); display: flex; align-items: center; justify-content: center; animation: modal-bg-show 0.3s;`;export const ModalBox = styled.div` width: 90%; max-width: 400px; margin: 0 auto; border-radius: 0.3rem; background-color: #fff; animation: ModalFade 0.3s; overflow: hidden;`;export const SignUpHeader = styled.div` position: relative; padding: 16px 64px 16px 16px; background-color: #f1f1f1; font-weight: 700; & > button { position: absolute; top: 15px; right: 15px; width: 30px; font-size: 21px; font-weight: 700; text-align: center; color: #999; border: 0; }`;export const SignUpBody = styled.div` padding: 10px; & > div { padding: 5px; display: flex; align-items: center; justify-content: center; } & > div > input { padding-top: 5px; padding-bottom: 5px; padding-left: 15px; padding-right: 15px; width: 290px; height: 30px; font-size: 14px; border: 1px solid rgb(222, 226, 230); }`;export const SignUpFooter = styled.div` border-top: 1px solid; padding: 7px 16px; text-align: right; & > button { padding: 6px 12px; color: #fff; background-color: #6c757d; border-radius: 5px; font-size: 13px; }`; import React, { useCallback, useState } from "react";import { ModalBox } from "../assets/css/login";import { ModalBackGround, SignUpBody, SignUpFooter, SignUpHeader,} from "../assets/css/signup";const SignUpTemplate = ({ open, onChangeOpen }) => { const [form, setForm] = useState({ username: "", email: "", password: "", }); const onChange = useCallback( (e) => { setForm({ ...form, [e.target.name]: e.target.value, }); }, [form] ); return open ? ( <ModalBackGround className="signup-modal-box"> <ModalBox> <SignUpHeader> 회원 가입 <button className="close" onClick={onChangeOpen}> {" "} &times;{" "} </button> </SignUpHeader> <SignUpBody> <div> <input type="username" id="username" name="username" placeholder="이름" onChange={onChange} /> </div> <div> <input type="email" id="email" name="email" placeholder="Email" onChange={onChange} /> </div> <div> <input type="password" id="password" name="password" placeholder="Password" onChange={onChange} /> </div> </SignUpBody> <SignUpFooter> <button onClick={onChangeOpen}>닫기</button> </SignUpFooter> </ModalBox> </ModalBackGround> ) : null;};export default SignUpTemplate;

0

React 로그인 Modal 만들기 2 - styled-component 사용하기

React 로그인 Modal 만들기 9 - Redux Saga 적용 React 로그인 Modal 만들기 8 - Redux 사용하기 React 로그인 Modal 만들기 7 - JWT 적용하기 React 로그인 Modal 만들기 6 - Login 구현 React 로그인 Modal 만들기 5 - 회원 가입 Back end 구현 React 로그인 Modal 만들기 4 - 비동기 처리 React 로그인 Modal 만들기 3 - 회원 가입 React 로그인 Modal 만들기 2 - styled-component 사용하기 React 로그인 Modal 만들기 React 로그인 Modal 만들기 2 - styled-component 사용하기기존에 style 내용들을 css 파일로 관리해도 문제가 없으나, React에서는 style마저도 js파일로 관리할 수 있도록 지원해주는 다양한 프레임 워크들이 있다. js파일로 관리하게 되면 React의 여러 기능을 사용하기 좋기 때문에 기존에 작성해준 Login 관련 css 내용을 js 파일로 변경하려고 한다. import styled, { keyframes } from "styled-components";export const ModalFade = keyframes`{ from { opacity: 0; margin-top: -50px; } to { opacity: 1; margin-top: 0; }}`;export const ModalBackGround = styled.div` position: fixed; top: 0; left: 0; width: 100%; height: 100%; background: rgba(0, 0, 0, 0.5); display: flex; align-items: center; justify-content: center;`;export const ModalBox = styled.div` width: 90%; max-width: 400px; margin: 0 auto; border-radius: 0.3rem; background-color: #fff; animation: ${ModalFade} 0.5s; overflow: hidden;`;export const LoginHeader = styled.div` position: relative; padding: 16px 64px 16px 16px; background-color: #f1f1f1; font-weight: 700; & > button { position: absolute; top: 15px; right: 15px; width: 30px; font-size: 21px; font-weight: 700; text-align: center; color: #999; border: 0; }`;export const LoginBody = styled.div` padding: 10px; & > div { padding: 5px; display: flex; align-items: center; justify-content: center; } & > div > input { padding-top: 5px; padding-bottom: 5px; padding-left: 15px; padding-right: 15px; width: 290px; height: 30px; font-size: 14px; border: 1px solid rgb(222, 226, 230); }`;export const LoginFooter = styled.div` border-top: 1px solid rgb(222, 226, 230); padding: 7px 16px; text-align: right; & > button { padding: 6px 12px; color: #fff; background-color: #6c757d; border-radius: 5px; font-size: 13px; }`; Styled-Component 적용하기import React, { useState, useCallback } from "react";import { LoginBody, LoginFooter, LoginHeader, ModalBackGround, ModalBox,} from "../assets/css/login";const LoginTemplate = ({ open, close }) => { const [form, setForm] = useState({ email: "", password: "", }); const onChange = useCallback( (e) => { setForm({ ...form, [e.target.name]: e.target.value, }); }, [form] ); return open ? ( <ModalBackGround> <ModalBox> <LoginHeader> 로그인 <button className="close" onClick={close}> {" "} &times;{" "} </button> </LoginHeader> <LoginBody className="login-body"> <div> <input id="email" name="email" type="email" placeholder="Email" onChange={onChange} /> </div> <div> <input id="password" name="password" type="password" placeholder="Password" onChange={onChange} /> </div> </LoginBody> <LoginFooter> <button onClick={close}>닫기</button> </LoginFooter> </ModalBox> </ModalBackGround> ) : null;};export default LoginTemplate;

0

React 로그인 Modal 만들기

React 로그인 Modal 만들기 9 - Redux Saga 적용 React 로그인 Modal 만들기 8 - Redux 사용하기 React 로그인 Modal 만들기 7 - JWT 적용하기 React 로그인 Modal 만들기 6 - Login 구현 React 로그인 Modal 만들기 5 - 회원 가입 Back end 구현 React 로그인 Modal 만들기 4 - 비동기 처리 React 로그인 Modal 만들기 3 - 회원 가입 React 로그인 Modal 만들기 2 - styled-component 사용하기 React 로그인 Modal 만들기 로그인 Modal 만들기기존에 개인 프로젝트로 로그인 창을 만들게 되면 별도의 웹 페이지로 로그인 화면을 구현을 했다. 하지만 좀 더 깔끔하고 반응적인 방법을 찾다가 Modal에 대해 알게 됐고 Modal을 이용해 Login 화면을 구현해 보고자 한다. 로그인 컴포넌트 만들기가장 바깥 Wrapper는 로그인 버튼을 누른 상태와 누르지 않은 상태를 보여줘야 함으로 조건절을 이용해 classname을 변경하도록 설정한다. // Login 페이지가 열리면 modal-wrapper가 선택된다. <div className={open ? "modal-wrapper" : null}> 내부 컴포넌트도 상태에 따라 보이거나 보이지 않게 상태를 변경해줄 필요가 있어 조건절을 통해 내부 컴포넌트를 가져올 수 있도록 설정한다. {open ? ( <div className="login-modal"> <div className="login-header">로그인</div> <div className="login-input"> <div className="login-id"> <input className="email" type="email" id="email" placeholder="Email" onChange={onChangeEmail} /> </div> <div className="login-password"> <input className="password" type="password" id="password" placeholder="Password" onChange={onChangePassword} /> </div> <div className="close-login"> <button className="close-button" onClick={close}> 닫기 </button> </div> </div> </div>) : null} LoginTemplate.js

0

Spring boot - MultipartFile 에서 발생하는 예외 처리

목차 Spring boot - StreamingResponseBody Spring boot - ResourceRegion Spring boot - 파일 다운로드 서비스 구현하기 Spring boot - 파일 업로드 서비스 구현하기 Spring boot - Resource 추상화 Spring boot - MultipartFile 에서 발생하는 예외 처리 Spring boot - MultipartFile 를 이용한 파일 업로드 Spring boot - Part 객체를 이용한 파일 업로드 MaxUploadSizeExceededException 예외MaxUploadSizeExceededException 예외가 발생하는 경우는 크게 두 가지가 있다. 첫 번째는 FileSizeLimitExceededException 예외가 발생했을 때 두 번째는 SizeLimitExceededException 예외가 발생했을 때다. FileSizeLimitExceededException 서버로 전송되는 각 파일 크기가 제한을 초과했을 때 발생하는 예외, 기본값은 1MB다.org.apache.tomcat.util.http.fileupload.impl.FileSizeLimitExceededException: The field file exceeds its maximum permitted size of 1048576 bytes. SizeLimitExceededException 서버로 전송되는 모든 파일 크기가 제한을 초과했을 때 발생하는 예외, 기본값은 10MB다.org.apache.tomcat.util.http.fileupload.impl.SizeLimitExceededException: the request was rejected because its size (29404104) exceeds the configured maximum (10485760)

0

HttpClient 사용하기 - Multipart Parameter 보내기

HttpClient 사용하기 HttpClient 사용하기 - Paramater 보내기 HttpClient 사용하기 - Multipart Parameter 보내기 HttpClient 사용하기 - Multipart Parameter 보내기 Client가 서버로 파일 을 전송하거나 한번에 여러개의 Form 데이터 를 보내기 위해 사용하는 방식이다. 이미지와 같은 Binary 데이터는 기존 application/x-www-form-urlencoded 나 application/json 과 같은 요청으로 적절치 않아 multipart/form-data 형태의 데이터로 요청을 보낸다. application/x-www-form-urlencoded 는 보내는 Paramater를 encoding 해서 전송한다. 데이터가 영숫자가 아닌 경우 3바이트로 표현하기 때문에 바이너리 파일을 전송할 경우 페이로드를 3배로 만들기 때문에 비효율 적이다. multipart/form-data 의 경우는 전송한 모든 문자를 인코딩하지 않은 형태로 보낸다. 의존성 추가하기// https://mvnrepository.com/artifact/org.apache.httpcomponents/httpmimeimplementation group: 'org.apache.httpcomponents', name: 'httpmime', version: '4.3.4' MultipartEntityBuilder 를 이용해 HttpEntity 객체 생성 MultipartEntityBuilder 를 이용해 쉽게 Text나 파일을 (Key, Value) 형태로 Request Body 에 넣을 수 있다. Text를 보낼때는 addTextBody 메소드를 이용해 값을 넣어주고 이미지나 파일 데이터를 보낼 때는 addBinaryBody 메소드를 이용해 Binary 파일 을 넣어주고 Content Type 으로 MULTIPART_FORM_DATA 를 명시해준다.

0

HttpClient 사용하기 - Paramater 보내기

HttpClient 사용하기 HttpClient 사용하기 - Paramater 보내기 HttpClient 사용하기 - Multipart Parameter 보내기 HttpClient 사용하기 - Paramater 보내기 Client가 서버로 요청과 함께 Paramater를 보내는 방법은 크게 Form 형태의 데이터를 보내는 방법과 JSON 형태의 데이터를 보내는 방법이 있다. 보통 Form 형태의 데이터는 (Key, Value) 형태의 데이터로 Http Body에 넣어서 보내고 JSON 형태의 데이터는 String 형태로 변환해 Http Body에 넣어서 보낸다. Form 형태의 데이터를 보낼 때 Header는 application/x-www-form-urlencoded 로 요청을 보내고 JSON 형태의 데이터를 보낼 때 Header는 application/json 로 요청을 보낸다. Paramater 요청 받을 수 있는 Back-end 코드/test/params 에 들어오는 Http 요청과 함께 오는 Paramater 내용을 찍어주는 로직을 작성해준다. Form 형태의 Paramater를 받을 때는 @RequestParam 을 이용해 데이터를 가져올 수 있다. @PostMapping("/test/params")public ResponseEntity testParams(@RequestParam("name") String name, @RequestParam("nickname") String nickName){ log.info("=========================== start ============================"); log.info("Name : " + name); log.info("Nickname : " + nickName); log.info("============================ end ============================="); return ResponseEntity.ok().build();} 요청시 보내는 Paramater 작성 요청시 보내는 Paramater는 NameValuePair 객체를 이용해 (Key, Value) 형태로 값을 넣어준다. 작성이 완료된 NameValuePair 객체를 이용해 HttpEntity 객체를 생성한다. HttpEntity 객체를 요청 객체 Entity에 넣어서 Http 요청을 보내면 요청시 Paramater도 같이 전달 된다. UrlEncodedFormEntity 를 이용해 만들어진 Entity에 대한 요청은 ContentType 이 application/x-www-form-urlencoded 으로 보내진다. List<NameValuePair> params = new ArrayList<>();params.add(new BasicNameValuePair("name", "test"));params.add(new BasicNameValuePair("nickname", "victor"));// NameValuePair 객체 이용해 HttpEntity 객체를 생성한다.HttpEntity entity = new UrlEncodedFormEntity(params);// 요청 객체에 Entity에 Paramater 넣어주기httpPost.setEntity(entity);