@Component
를 사용하면@ComponentScan
에 의해 자동으로 스캔되어 해당 클래스를 Bean으로 자동으로 등록해준다.- 비즈니스 로직과 관련된 클래스들은 그 수가 많기 때문에
@Controller
,@Service
와 같은 애너테이션들을 사용해서 Bean으로 등록하고 관리하면 개발 생산성에 유리하다.
이러한 자동의 장점들이 있음에도 수동으로 Bean을 등록해야 할때도 있다.
- 기술적인 문제나 공통적인 관심사를 처리할 때 사용하는 객체들을 수동으로 등록하는 것이 좋다.
@Configuration public class PasswordConfig { @Bean public PasswordEncoder passwordEncoder() { return new BCryptPasswordEncoder(); } }
- Bean으로 등록하고자하는 객체를 반환하는 메서드를 선언하고
@Bean
을 설정한다.- Bean을 등록하는 메서드가 속한 해당 클래스에
@Configuration
을 설정한다.- Spring 서버가 뜰 때 Spring IoC 컨테이너에 'Bean'으로 저장된다.
자동이던 수동이던 Bean을 등록하다가 같은 타입의 Bean이 등록되면 어떻게 될까?
테스트를 위해 Food 타입의 pizza와 chicken을 등록하고 사용하려고 하면 다음과 같은 에러가 발생한다.
Food food;
필드에@Autowired
를 사용하여 Bean 객체를 주입려고 했으나, Food 타입의 Bean 객체가 하나 이상 있기때문에 어떤 Bean을 주입해줘야할지 몰라서 발생한 오류이다.
이 오류를 해결하는 방법을 알아보자.
@Autowired Food pizza; @Autowired Food chicken;
이렇게 등록된 Bean의 이름을 정확하게 명시해주면 해결할 수 있다.
@Component @Primary public class Chicken implements Food { @Override public void eat() { System.out.println("치킨을 먹습니다."); } }
@Primary
가 추가되면 같은 타입의 Bean이 여러 개 있더라도 우선@Primary
가 설정된 Bean 객체를 주입 해준다.
@Component @Qualifier("pizza") public class Pizza implements Food { @Override public void eat() { System.out.println("피자를 먹습니다."); } } @SpringBootTest public class BeanTest { @Autowired @Qualifier("pizza") Food food; }
@Qualifier
는@Primary
와 동시에 적용되어 있어도@Qualifier
의 우선순위가 더 높아@Qualifier
가 적용된다.- 하지만 Bean을 주입 받고자 하는 곳에도
@Qualifier
를 추가해야한다는 번거로움이 있다.- 따라서 같은 타입의 Bean이 여러 개 있을 때는 범용적으로 사용되는 Bean 객체에는
@Primary
를 설정하고 지엽적으로 사용되는 Bean 객체에는@Qualifier
를 사용하는 것이 좋다.
- 인증은 해당 유저가 실제 유저인지 인증하는 개념이다.
- 지문인식, 로그인 등과 같이, 실제 그 유저가 맞는지를 확인하는 절차이다.
- 인가는 해당 유저가 특정 리소스에 접근이 가능한지 허가를 확인하는 개념이다.
- 관리자 페이지, 관리자 권한 같은 것들을 예로 들 수 있다.
- 사용자가 로그인 요청을 보낸다.
- 서버는 DB의 유저 테이블과 아이디 비밀번호를 대조한다.
- DB의 유저테이블의 정보와 일치한다면 인증을 통과한 것으로 보고 “세션 저장소”에 해당 유저가 로그인 되었다는 정보를 넣는다.
- 세션 저장소에서는 유저의 정보와는 관련 없는 난수인
session-id
를 발급한다.
- 서버는 로그인 요청의 응답으로
session-id
를 내어준다.
- 클라이언트는 그
session-id
를 쿠키라는 저장소에 보관하고 앞으로의 요청마다 세션아이디를 같이 보낸다.
- 클라이언트의 요청에서 쿠키를 발견했다면 서버는 세션 저장소에서 쿠키를 검증한다.
- 세션 저장소에서 유저정보를 받아온다.
- 유저정보에 따른 응답을 내어준다.
public static void addCookie(String cookieValue, HttpServletResponse res) { try { cookieValue = URLEncoder.encode(cookieValue, "utf-8").replaceAll("\\+", "%20"); // Cookie Value 에는 공백이 불가능해서 encoding 진행 Cookie cookie = new Cookie(AUTHORIZATION_HEADER, cookieValue); // Name-Value cookie.setPath("/"); cookie.setMaxAge(30 * 60); // Response 객체에 Cookie 추가 res.addCookie(cookie); } catch (UnsupportedEncodingException e) { throw new RuntimeException(e.getMessage()); } }
- 클라이언트에 저장될 목적으로 생성한 작은 정보를 담은 파일이다.
- 클라이언트 (웹 브라우져)에 저장된다.
- 쿠키 저장 시 만료일시 설정 가능하다. 브라우져가 종료되어도 유지된다.
- 클라이언트에 저장되기 때문에 보안에 취약하다.
@GetMapping("/create-session") public String createSession(HttpServletRequest req) { // 세션이 존재할 경우 세션 반환, 없을 경우 새로운 세션을 생성한 후 반환 HttpSession session = req.getSession(true); // 세션에 저장될 정보 Name - Value 를 추가 session.setAttribute(AUTHORIZATION_HEADER, "Robbie Auth"); return "createSession"; } @GetMapping("/get-session") public String getSession(HttpServletRequest req) { // 세션이 존재할 경우 세션 반환, 없을 경우 null 반환 HttpSession session = req.getSession(false); String value = (String) session.getAttribute(AUTHORIZATION_HEADER); // 가져온 세션에 저장된 Value 를 Name 을 사용하여 가져옴 System.out.println("value = " + value); return "getSession : " + value; }
- 서버에서 일정시간 동안 클라이언트 상태를 유지하기 위해 사용한다.
- 웹 서버에 저장된다.
- 다음 조건 중 하나가 만족될 경우 만료된다.
- 브라우져 종료 시까지
- 클라이언트 로그아웃 시까지
- 서버에 설정한 유지기간까지 해당 클라이언트의 재요청이 없는 경우
- 웹 서버에 저장되기 때문에 상대적으로 보안이 안전하다.
오늘은 백엔드의 핵심 중 하나인 로그인부분을 공부했다.
그동안 프로젝트 하면서 로그인쪽은 맡아보지 않았었는데 이런식으로 동작하는구나~ 하면서 재밌게 공부했다. 매번 새로운걸 배워서 그런지 재밌다 :)