JAVA/SPRING

[Spring] Spring Security setting๊ณผ ์—๋Ÿฌ ์‚ฌํ•ญ ์ˆ˜์ • ๊ณผ์ • ๊ธฐ๋ก๊ณผ OAuth2

2023. 8. 10. 11:41
๋ชฉ์ฐจ
  1. Security  docs : 
  2. ์ž‘์—…ํ•œ ํ”„๋กœ์ ํŠธ ๋ฒ„์ „ 
  3. Spring Security์˜ ์ œ๊ณต ๊ธฐ๋Šฅ 
  4. ๋กœ๊ทธ์ธ์„ ํ†ตํ•œ ์ธ์ฆ ๊ตฌ์กฐ
  5. ์ž‘์—…ํ•œ ํ”„๋กœ์ ํŠธ์˜  SecurityConfig
  6. ์ธ์ฆ ํ•„ํ„ฐ๋Š”?
  7. ์ธ๊ฐ€ ํ•„ํ„ฐ๋Š”? 
  8. OAuth๋ฅผ ํ†ตํ•œ ๋กœ๊ทธ์ธ
  9. Authorization Code์™€ Implicit Grant(์•”๋ฌต์  ์Šน์ธ)
728x90
๋ฐ˜์‘ํ˜•

Security  docs : 

 

Spring Security

Spring Security is a powerful and highly customizable authentication and access-control framework. It is the de-facto standard for securing Spring-based applications. Spring Security is a framework that focuses on providing both authentication and authoriz

spring.io


๐Ÿบ ์ด๊ฑฐ ์™œ ? ํ”„๋กœ์ ํŠธ ์ง„ํ–‰ ์ค‘ ํŠน์ • API๋ฅผ ์™ธ๋ถ€์—์„œ ์“ธ ์ˆ˜ ์žˆ๋„๋ก ์ธ์ฆ๊ณผ์ •์„ ์ œ๊ฑฐํ•ด๋‹ฌ๋ผ๋Š” ์š”์ฒญ์ด ์žˆ์—ˆ๊ณ , ์ œ๊ฑฐ ํ›„ ํ”„๋กœ์ ํŠธ๋ฅผ ์Šคํ…Œ์ด์ง€ ์„œ๋ฒ„์—์„œ ๊ธฐ๋™ 

-> API๋Š” ์ž์œ ๋กญ๊ฒŒ ์“ธ ์ˆ˜ ์žˆ์—ˆ์œผ๋‚˜, ๊ด€๋ จ๋œ ๋‚ด๋ถ€ ๋ฉ”์†Œ๋“œ์—์„œ ์ธ์ฆ์ด ์—†๋Š” ๊ฒฝ์šฐ ํŠน์ • ๊ธฐ๋Šฅ์„ ์‚ฌ์šฉํ•˜์ง€ ๋ชปํ•˜๋Š” ์ƒํ™ฉ์ด ๋ฐœ์ƒ. 

 

Security ์™€ ์ธ์ฆ์— ๋Œ€ํ•ด ์ •๋ฆฌ ํ•˜๊ณ  ๋„˜์–ด๊ฐ€๊ธฐ๋กœ ํ•จ.

 

 

์ž‘์—…ํ•œ ํ”„๋กœ์ ํŠธ ๋ฒ„์ „ 

Spring-boot :

  • Maven: org.springframework.security.oauth:spring-security-oauth2:2.3.4.RELEASE
  • Maven: org.springframework.security:spring-security-config:5.5.1
  • Maven: org.springframework.security:spring-security-core:5.5.1
  • Maven: org.springframework.security:spring-security-crypto:5.5.1
  • Maven: org.springframework.security:spring-security-web:5.5.1

JAVA : 1.8 

 

Security & Oauth : 

  • Maven: org.springframework.security.oauth:spring-security-oauth2:2.3.4.RELEASE
    • -> SPRING SECURITY 5.0๋ถ€ํ„ฐ 2.0๋Œ€ ๋ฒ„์ „ ์‚ฌ์šฉ ๊ฐ€๋Šฅ.
  • Maven: org.springframework.security:spring-security-config:5.5.1
  • Maven: org.springframework.security:spring-security-core:5.5.1
  • Maven: org.springframework.security:spring-security-crypto:5.5.1
  • Maven: org.springframework.security:spring-security-web:5.5.1

 

 

 


Spring Security์˜ ์ œ๊ณต ๊ธฐ๋Šฅ 

  1. Servlet API ํ†ตํ•ฉ
  2. Spring Web MVC์™€์˜ ์„ ํƒ์  ํ†ตํ•ฉ
  3. ์ธ์ฆ ๊ถŒํ•œ ๋ถ€์—ฌ๋ฅผ ํฌ๊ด„์ ๋กœ ํ™•์žฅํ•˜๋ฉฐ ์ง€์›
  4. ์„ธ์…˜ ๊ณ ์ •, clickjacking, ์‚ฌ์ดํŠธ๊ฐ„ ์š”์ฒญ ์œ„์กฐ(CSRF) ๋ฐฉ์ง€

์›น ๊ธฐ๋ฐ˜ ์ธ์ฆ ๋ฐ ์ธ๊ฐ€๋ฅผ ํŽธํ•˜๊ฒŒ ๊ตฌํ˜„ํ•˜๋„๋ก ํ•จ.

 

๐Ÿค– stateless ํ•œ HTTP์œ„์—์„œ ์ธ์ฆ ์ธ๊ฐ€๊ฐ€ ์—†๋‹ค๋ฉด ๋น„๋ฐ€๋ฒˆํ˜ธ์™€ ์•„์ด๋””๋ฅผ ๊ณ„์† ์ž…๋ ฅํ•ด์•ผ ํ•˜๋Š”  ๋ถˆ์ƒ์‚ฌ๊ฐ€ ์ƒ๊ธธ ์ˆ˜ ๋„์žˆ๋‹ค.

 

์ธ์ฆ? : ์‚ฌ์šฉ์ž์˜ ์‹ ์› ํ™•์ธ 

์ธ๊ฐ€? : ์‚ฌ์šฉ์ž๊ฐ€ ํŠน์ • ๋ฆฌ์†Œ์Šค์— ์ ‘๊ทผํ•˜๋Š” ๊ถŒํ•œ ๋˜๋Š” ๋™์ž‘ ์ˆ˜ํ–‰ ๊ฐ€๋Šฅ์„ฑ์„ ์ง€๋‹ˆ๊ณ  ์žˆ๋Š” ์ง€ ๊ฒ€์ฆ - ์ ‘๊ทผ ๊ถŒํ•œ์„ ์–ป๋Š” ์ผ

 

๊ทธ๋ž˜์„œ ๋ณดํ†ต ์ฟ ํ‚ค / ์„ธ์…˜ / ํ† ํฐ ๋ฐฉ์‹์œผ๋กœ ํ•ด๊ฒฐํ•˜์ง€๋งŒ ํ•ด๊ฒฐ๋ฐฉ์‹์— ๋Œ€ํ•ด์„œ๋Š” ์ƒ๋žต. 

 

 

 

 

 

 

๐Ÿค– ์ธ์ฆ ๊ณผ์ •์€ ๋‹ค์Œ๊ณผ ๊ฐ™๋‹ค.

 


๋กœ๊ทธ์ธ์„ ํ†ตํ•œ ์ธ์ฆ ๊ตฌ์กฐ

 

 

 

  1. ์‚ฌ์šฉ์ž์˜ ์š”์ฒญ ( Request ) โ†’ ์ธํ„ฐ์…‰ํŠธ โ†’
  2. UsernamePasswordAuthenticationToken ์ƒ์„ฑ
  3. ์ธ์ฆ ๋‹ด๋‹น โ€” ์•„์ด๋”” - PW ( post -form ๋ฐ์ดํ„ฐ๋กœ ์ƒ์„ฑ ) โ€” Authentication ๊ฐ์ฒด ์ƒ์„ฑ - ๋‹ค๋ฅธ ํ•„ํ„ฐ๋กœ ์ „๋‹ฌํ•˜๊ธฐ ์œ„ํ•จ.
    1. ์„ฑ๊ณต ์‹œ, ์ดํ›„ ํ”„๋กœ์„ธ์Šค ์ง„ํ–‰
    2. ์‹คํŒจ ์‹œ, throws Authentication Exception
  4. AuthenticationProvider์— ๊ฐ์ฒด ์ „๋‹ฌ
  5. UserDetailsService์—์„œ Authentication ๊ฐ์ฒด๋ฅผ DB๋‚ด์— ์กด์žฌํ•˜๋Š” ์œ ์ €์ž„์„ ์กฐํšŒ
    1. ์œ ์ € ์„ธ์…˜์„ ์ƒ์„ฑ - ๋ฆฌํ„ด ๋ฆฌํ„ด ๋ฆฌํ„ด 
  6. ์‹œํ๋ฆฌํ‹ฐ์ปจํ…์ŠคํŠธ(์ธ๋ฉ”๋ชจ๋ฆฌ) ํ™€๋”์— ์ €์žฅ
  7. ์œ ์ € ์„ธ์…˜ ID์™€ ํ•จ๊ป˜ ์‘๋‹ต์„ ๋ณด๋‚ธ๋‹ค.

์ดํ›„ ์‚ฌ์šฉ์ž์˜ ์š”์ฒญ(์ฟ ํ‚ค) ์—์„œ JsessionID๋ฅผ ๊ฒ€์ฆ ํ›„ ์œ ํšจํ•˜๋‹ค๋ฉด ์ธ์ฆ์ฒ˜๋ฆฌ.

 

 

 


์ž‘์—…ํ•œ ํ”„๋กœ์ ํŠธ์˜  SecurityConfig

 

๐Ÿค– Security 3.2 ๋ฒ„์ „ ์ด์ƒ๋ถ€ํ„ฐ๋Š” XML ์„ค์ •๋ณด๋‹ค ์กฐ๊ธˆ ๋” ํŽธํ•œ ์ž๋ฐ” ๊ธฐ๋ฐ˜ ์–ด๋…ธํ…Œ์ด์…˜ ์„ค์ •(EnableWebSecurity)๋„ ๊ฐ€๋Šฅํ•˜๋‹ค.

 

 

๐Ÿค– SecurityConfig๋Š” WebSecurityConfigurerAdapter์˜ ๊ตฌํ˜„์ฒด์ด๋‹ค.

 

 

 

 

 

 

๋‹ค์Œ๊ณผ ๊ฐ™์ด ์ปจํŠธ๋กค๋Ÿฌ์—์„œ ์‚ฌ์šฉ ํ•  ์ˆ˜ ์žˆ๋Š” ์–ด๋…ธํ…Œ์ด์…˜๋„ ์žˆ๋‹ค.

 

 

 

 

 

 

 

 

 

๐Ÿค– ์–ด๋…ธํ…Œ์ด์…˜์— ์˜ํ•œ ์„ค์ •์ด๋ผ ํ•˜์—ฌ

ํฐ ์ฐจ์ด๊ฐ€ ์žˆ๋Š” ๊ฒƒ์€ ์•„๋‹ˆ๋ฉฐ,  ์ปจํŠธ๋กค๋Ÿฌ ๋ณ„๋กœ ์•„๋ž˜์˜ configure์—์„œ ์„ธํŒ…ํ•˜๋Š” Role์„ ์ปจํŠธ๋กค๋Ÿฌ ๋ณ„๋กœ ์„ธํŒ… ํ•  ์ˆ˜ ์žˆ๋‹ค๋Š” ์ฐจ์ด์ ์ด ์žˆ๋‹ค. 

 

์•„๋ž˜์™€ ์„ธํŒ…์ด ์ปจํŠธ๋กค๋Ÿฌ ์–ด๋…ธํ…Œ์ด์…˜ ์„ธํŒ…๊ณผ ๋™์ผํ•˜๋‹ค๋ฉด, ์‹œํ๋ฆฌํ‹ฐ ์„ค์ •์— ์˜ํ•œ ๋™์ž‘์€ ๋™์ผํ•˜๋‹ค.

 

 

 

๐Ÿค– ๋‹ค์Œ์€ ์ž‘์—…์ค‘์ธ ํ”„๋กœ์ ํŠธ์˜ Configure ์„ค์ •์ด๋‹ค. 

  

์ธ์ฆ๊ณผ ์ธ๊ฐ€๋ฅผ ๋™์‹œ์— ํ•„ํ„ฐ๋กœ ์ฒดํฌํ•˜๊ณ ์žˆ๋‹ค. 

 

 

 

 

 

 

๐Ÿค–  ์ด๋ฒˆ  ํŠน์ • target API ์ธ์ฆ ์ œ๊ฑฐ ์ž‘์—… ์‹œ ๋ฌธ์ œ๊ฐ€ ๋˜์—ˆ๋˜ ๋ถ€๋ถ„์€ ๊ฑฐ์˜ ๋Œ€๋ถ€๋ถ„์˜ API์˜ ์‹œ์ž‘์ง€์ ์ธ /API/Studio/**์˜ ๋ถ€๋ถ„์ด๋‹ค.

 

 

๐Ÿค– ํŠน์ • target API์— ๋Œ€ํ•œ ์ธ์ฆ ์ œ๊ฑฐ ์„ค์ •๊ณผ ๊ธฐ์กด์˜ API ์‹œ์ž‘์ง€์ ์— ๋Œ€ํ•œ ๋ชจ๋“  ์ธ์ฆ ์š”๊ตฌ ์„ค์ •์ด ์ถฉ๋Œํ•œ๋‹ค.

 

๐Ÿค– ์ž‘์—… ์‹œ์—๋Š” ๊ฐ•์ œ๋กœ target API์— ๋Œ€ํ•œ ์ธ์ฆ์„ ์ œ๊ฑฐํ–ˆ์ง€๋งŒ API๋‚ด์˜ ๋น„์ฆˆ๋‹ˆ์Šค ๋กœ์ง์—์„œ ์ธ์ฆ์— ๋”ฐ๋ฅธ ์‚ฌ์šฉ์ž ์ •๋ณด๋ฅผ ์บ์น˜ํ•˜๋Š” ๋ถ€๋ถ„์—์„œ ์—๋Ÿฌ๊ฐ€ ๋‚˜์„œ ์šฐํšŒํ•˜๋Š” ์‹์œผ๋กœ ์ž‘์—…์„ ์ˆ˜์ •ํ–ˆ๋‹ค. --> ๋ฏธ๋ฆฌ ํ™•์ธํ•˜๊ณ  ์ˆ˜์ •ํ–ˆ์–ด์•ผ ํ–ˆ๋Š”๋ฐ ์ฝ”๋“œ๋ฅผ ๊ผผ๊ผผํžˆ ๋ณด์ง€ ์•Š๊ณ  ์ž‘์—…๋ถ€ํ„ฐ ํ•œ๊ฒŒ ์š”์ธ..

 

 

 

 

 

 

๐Ÿค– configure ์„ค์ •์—์„œ๋Š” JwtAuthenticationFilter ์™€ JwtAuthorizationFilter๋ฅผ ์‚ฌ์šฉํ•˜๋Š”๋ฐ ๋‹ค์Œ์—์„œ ๋‘˜ ๋‹ค ์‚ดํŽด๋ณธ๋‹ค.

 

์ธ์ฆ ํ•„ํ„ฐ๋Š”?

 

 

์™€ ๊ฐ™์ด UsernamePasswordAuthenticationFilter๋ฅผ ํ™•์žฅํ•˜๊ณ  ์žˆ๋‹ค. 

 

๐Ÿค– UsernamePasswordAuthenticationFilter๋Š”  ์‚ฌ์šฉ์ž์˜ request์—์„œ ์•„์ด๋””์™€ ํŒจ์Šค์›Œ๋“œ๋ฅผ ํŒŒ์‹ฑํ•ด์„œ ์ธ์ฆ ์š”์ฒญ(UsernamepasswordAuthentication Token์„ ์ƒ์„ฑ -> Authenticationmanager๋ฅผ ๊ตฌํ˜„ํ•œ ๊ฐ์ฒด์— ์ธ์ฆ ์œ„์ž„)์„ ํ•œ๋‹ค.

์ด๋Š”, 

 

์™€ ๊ฐ™์ด ๊ตฌํ˜„ํ•˜๊ณ , ๋‹ค์Œ๊ณผ ๊ฐ™์€ ๊ณผ์ •์„ ๊ฑฐ์นœ๋‹ค๊ณ  ๋ณด๋ฉด ๋œ๋‹ค.

 

 

// UserDetailService์˜ ์ƒ์†๊ณผ ๊ตฌํ˜„์ฒด๊ฐ€ ์กด์žฌํ•ด์•ผ ํ•œ๋‹ค.
Authentication authentication = this.getAuthenticationManager().authenticate(authenticationToken);

ํ”„๋กœ๋ฐ”์ด๋”๋Š” ์‹ค์ œ ์‚ฌ์šฉ์ž ์—ฌ๋ถ€๋ฅผ ๋ฐ˜๋“œ์‹œ ์ฒดํฌํ•ด์•ผ ํ•œ๋‹ค.

 

๐Ÿค– ์œ„ ๊ณผ์ •์—์„œAuthenticationManager๋ฅผ ํ†ตํ•œ ๋กœ๊ทธ์ธ ์‹œ๋„๋Š” UserDetailService๋ฅผ ์ƒ์†๋ฐ›์€ PrincipalDetailService๋ฅผ ํ˜ธ์ถœํ•˜๊ณ , 

loadUserByUserName ๋ฉ”์†Œ๋“œ๋ฅผ ํ†ตํ•ด ์„œ๋น„์Šค์˜ DB์— ์žˆ๋Š” ์œ ์ €์™€ ์ผ์น˜ํ•˜๋Š”์ง€ ์ฒดํฌํ•œ๋‹ค.

 

@Service
@RequiredArgsConstructor
public class PrincipalDetailService implements UserDetailsService {

    private final AuthRepository AuthRepository;

    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        String loginId = username;
        Account userEntity = AuthRepository.selectUserByLoginId(loginId);
        if (ICNObjectUtility.isEmpty(userEntity)) {
            throw new UsernameNotFoundException("UsernameNotFoundException!!");
        } else {
            return new PrincipalDetails(userEntity);
        }
    }
}

 

 

 

๐Ÿค– ๋‹ค์Œ์€ 4๋ฒˆ ๊ณผ์ •์ธ ์•„์ด๋””์™€ ํŒจ์Šค์›Œ๋“œ๋ฅผ ํ†ตํ•œ ์ธ์ฆ ์„ฑ๊ณต์‹œ์˜ ๋กœ์ง์ด๋‹ค. 

 

 

 

์ž‘์—…์ค‘์ธ ํ”„๋กœ์ ํŠธ๋Š” ์ฟ ํ‚ค๋ฅผ ํ†ตํ•œ ์ธ์ฆ ๋ชจ๋“œ์™€ ํ—ค๋”๋ฅผ ํ†ตํ•œ ์ธ์ฆ๋ชจ๋“œ๋กœ ๋‚˜๋ˆ ์ ธ ์žˆ๋‹ค ( ์„ค์ •๊ฐ’ properties๋กœ ์ œ์–ด)

 

์ธ์ฆ ์„ฑ๊ณต ์ดํ›„ ๋ ˆ๋””์Šค ์บ์‹œ ์„œ๋ฒ„์— ํ† ํฐ๊ณผ ๊ณ„์ • ๋กœ๊ทธ์ธ ์•„์ด๋”” ๋“ฑ์„ ์„ธํŒ…ํ•œ๋‹ค.

 

// ํ† ํฐ ์ƒ์„ฑ 
accessToken = this.jwtUtility.generateToken(principalDetails, expTmAccess);
refreshToken = this.jwtUtility.generateToken(principalDetails, expTmRefresh);

// ์ฟ ํ‚ค
accessToken = this.jwtUtility.generateToken(principalDetails, expTmAccess);
refreshToken = this.jwtUtility.generateToken(principalDetails, expTmRefresh);

// response
response.addCookie(accessCookie);
response.addCookie(refreshCookie);

// redis ์— ์ ์žฌ 
redisUtility.setData(refreshToken, principalDetails.getAccount().getLoginId(), Long.valueOf(expTmRefresh));

๋ ˆ๋””์Šค์— Data ์ƒ์„ฑํ•ด์„œ ๊ธฐ๋กํ•˜๊ณ , ๋ฆฌํ„ดํ•œ๋‹ค.

 

 

 

 

 

 

 

์ธ๊ฐ€ ํ•„ํ„ฐ๋Š”? 

 

 

BasicAuthenticationFilter๋ฅผ ํ™•์žฅํ•œ๋‹ค. 

 

 

 

์ธ์ฆ๊ณผ ๋งˆ์ฐฌ๊ฐ€์ง€๋กœ ์ž‘์—…์ค‘์ธ ํ”„๋กœ์ ํŠธ์—์„œ properties๋กœ ๊ด€๋ฆฌํ•˜๋Š” ์ธ์ฆ ๋ชจ๋“œ์— ๋”ฐ๋ผ ๋ถ„๊ธฐ๊ฐ€ ๋‚˜๋‰œ๋‹ค. (์ƒ๋žต)

 

์—ฌ๊ธฐ์„œ๋„ ๋งˆ์ฐฌ๊ฐ€์ง€๋กœ ํ—ค๋” ์ฟ ํ‚ค ์ธ๊ฐ€ ๊ฒ€์ฆ์œผ๋กœ ๋ณธ๋‹ค. 

 

try {
    // request์˜ ํ† ํฐ์„ ๊ฒ€์ฆ (์ฟ ํ‚ค)
    Cookie accessCookie = this.cookieUtility.getCookie(request, "accessToken");
    Cookie refreshCookie = this.cookieUtility.getCookie(request, "refreshToken");

    // Secret ํ‚ค๋ฅผ ํ†ตํ•œ JWT ๊ฒ€์ฆ
    loginId = JWT.require(Algorithm.HMAC512(this.Properties.getSecret())).build().verify(accessToken).getClaim("loginId").asString();

    // ๋กœ๊ทธ์ธ ์‚ฌ์šฉ์ž ๊ฒ€์ฆ 
     Account userEntity = commAuthRepository.selectUserByLoginId(loginId);
    PrincipalDetails principalDetails = new PrincipalDetails(userEntity);
    Authentication authentication = new UsernamePasswordAuthenticationToken(principalDetails, null, principalDetails.getAuthorities());

	// security session์— ์ ‘๊ทผํ•ด์„œ Autehntication ๊ฐ์ฒด๋ฅผ ์ €์žฅํ•œ๋‹ค.
	SecurityContextHolder.getContext().setAuthentication(authentication);

	// ์ €์žฅํ•œ ๊ฐ์ฒด๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™์ด ๊บผ๋‚ด์˜ฌ ์ˆ˜ ์žˆ๋‹ค!
    // SecurityContextHolder.getContext().getAuthentication(authentication);

	// ์„ฑ๊ณต์ผ ๊ฒฝ์šฐ ๋‹ค์Œ ๊ณผ์ • ์ง„ํ–‰
	chain.doFilter(request, response);

} catch (JWTVerificationException e) {
	// ํ† ํฐ ๊ฒ€์ฆ์‹œ ์‹คํŒจํ•˜๊ฑฐ๋‚˜ ๋งŒ๋ฃŒ ์ธ๊ฒฝ์šฐ refresh token ๊ฒ€์ฆ
    refreshCookie = this.cookieUtility.getCookie(request, "refreshToken");

    // redis ์„œ๋ฒ„์—์„œ refreshToken์ด ์กด์žฌํ•˜๋Š”์ง€ ํ™•์ธํ•˜๊ณ , 
    String redisLoginId = redisUtility.getData(refreshToken);

    // ๋‹ค์‹œ ์•ก์„ธ์Šค ํ† ํฐ์„ ๋ฐœ๊ธ‰ํ•œ๋‹ค.
	 String newAccessToken = this.jwtUtility.generateToken(principalDetails, expTmAccess);
    Cookie newAccessCookie = this.cookieUtility.createTokenCookie("accessToken", newAccessToken, expTmRefresh);
    response.addCookie(newAccessCookie);
    
} catch (Exception e ) {
	// ์ƒ๋žต..
}

 

๐Ÿค– ๊ฐ์ฒด๋ฅผ ์ €์žฅํ•˜๊ณ  ๊บผ๋‚ด์˜ค๋ฉฐ ์‚ฌ์šฉ์ž์˜ ์ •๋ณด์— ๋Œ€ํ•ด ๋งŽ์€ ๊ฒƒ์„ ์•Œ ์ˆ˜ ์žˆ๊ฒŒ ๋˜๋Š”๋ฐ,

 

๐Ÿค– ์ด ๊ณผ์ •์ด ์ด๋ฒˆ ์ˆ˜์ • ์‚ฌํ•ญ์—์„œ ๋งŽ์€ ๋ฌธ์ œ๋ฅผ ์ผ์œผํ‚จ ๋ถ€๋ถ„์ด๋‹ค. 

 

ํ˜„์žฌ ํ”„๋กœ์ ํŠธ ๋‚ด์—์„œ๋Š” ๋‹ค์Œ๊ณผ๊ฐ™์ด ์„ธ์…˜์— ์ €์žฅ๋œ ์‚ฌ์šฉ์ž ์ •๋ณด๋ฅผ ๊ฐ€์ ธ์˜ค๋Š” ์ฝ”๋“œ๊ฐ€ ์กด์žฌํ•˜๋Š”๋ฐ, 

 

๋ฌธ์ œ๋Š” ์ด ์„ธ์…˜ ์ •๋ณด๋ฅผ ๊ฐ€์ ธ์˜ค๋Š” ์‚ฌ์šฉ์ฒ˜๊ฐ€ 

 

 

์ •๋ง ๋งŽ๋‹ค...

 

 

๐Ÿ… ๋‹น์—ฐํžˆ ๋‚ด๊ฐ€ ์ž‘์—…ํ–ˆ๋˜ ์ธ์ฆ ์ธ๊ฐ€ ์ƒ๋žตํ•˜๋Š” ํŠน์ • API OPEN ์ž‘์—…์€ 

   ์‚ฌ์šฉ์ž๊ฐ€ ์ตœ์ดˆ ์ ‘๊ทผ -

   configurer์— ์˜ํ•œ  filter (์ธ์ฆ ๋ฐ ์ธ๊ฐ€) -

   ์ธ๊ฐ€ session ์— ์‚ฌ์šฉ์ž ์ •๋ณด ์ €์žฅ -

   controller api ํ˜ธ์ถœ  -

   ๋น„์ฆˆ๋‹ˆ์Šค -

   ์ข…๋ฃŒ     

 

     ๋˜๋Š” ๋น„์ฆˆ๋‹ˆ์Šค - ( ์‚ฌ์šฉ์ž ๊ฒ€์ฆ ๋ฐ ์‚ฌ์šฉ์ž ์ •๋ณด๋ฅผ ๋ฐ”ํƒ•์œผ๋กœ ํ•œ ๋น„์ฆˆ๋‹ˆ์Šค ๋กœ์ง )

 

๊ณผ ๊ฐ™์€ ๋ฐฉ์‹์—์„œ ๋‘ ๋ฒˆ์งธ ์ผ€์ด์Šค์ธ ์‚ฌ์šฉ์ž ์ •๋ณด๋ฅผ ์“ฐ๋Š” ๊ฒฝ์šฐ ๋ฌธ์ œ๊ฐ€ ๋œ๋‹ค.

 

 

์ˆ˜์ •ํ•œ ์ธ์ฆ๊ณผ ์ธ๊ฐ€๋ฅผ ์ƒ๋žตํ•œ API์— ์ ‘๊ทผ(๋‹น์—ฐํžˆ ์ƒ๋žต๋์œผ๋‹ˆ Session์—๋Š” ์‚ฌ์šฉ์ž ์ •๋ณด๊ฐ€ ์—†๋‹ค!) ํ–ˆ๋Š”๋ฐ,  ์‚ฌ์šฉ์ž ์ •๋ณด๋ฅผ ๊ฒ€์ฆํ•˜๋Š” ๋น„์ฆˆ๋‹ˆ์Šค ๋กœ์ง์ด ์ˆจ์–ด์žˆ๋‹ค๋ฉด? -> null point Exception์„ ๋ฐฉ์–ดํ•˜์ง€ ์•Š์„ ๊ฒฝ์šฐ ๊ทธ๋Œ€๋กœ 500 Error ๋ฐœ์ƒ ํ˜น์€ ๋ฐฉ์–ด ์ฝ”๋“œ์ธ ๊ฒฝ์šฐ ์ •์ƒ์ ์ธ ์ ‘๊ทผ์ด ์•„๋‹ˆ๋ผ๋Š” Return์„ ๋ฐ›๊ธฐ ๋•Œ๋ฌธ์ด๋‹ค.

 

 

 

๋”ฐ๋ผ์„œ, target API๋งŒ!   ๋ฐ˜๋“œ์‹œ ํ•„์š”ํ•  ๊ฒฝ์šฐ ์‚ฌ์šฉ์ž ์ •๋ณด๋ฅผ ์ƒ๋žตํ•˜๋Š” ์‹์œผ๋กœ ์ˆ˜์ •๋ฐฉํ–ฅ์„ ์žก์•˜๋‹ค.

๋˜๋Š” ๋™์ผํ•œ ๊ธฐ๋Šฅ์„ ํ•˜๋Š” ์‹ ๊ทœ API ์ƒ์„ฑ ( ํ•‰ํ„ฐ์˜ ๋ฒ”์œ„์—์„œ ์ œ์™ธ๋œ URI๋กœ)

 


๊ทธ ์™ธ.

 

OAuth๋ฅผ ํ†ตํ•œ ๋กœ๊ทธ์ธ

 

 

Oauth : ๊ตฌ๊ธ€, ํŽ˜์ด์Šค๋ถ, ํŠธ์œ„ํ„ฐ์™€ ๊ฐ™์€ ๋‹ค์–‘ํ•œ ํ”Œ๋žซํผ์˜ ํŠน์ •ํ•œ ์‚ฌ์šฉ์ž ๋ฐ์ดํ„ฐ์— ์ ‘๊ทผํ•˜๊ธฐ ์œ„ํ•ด ์ œ3์ž ํด๋ผ์ด์–ธํŠธ(์šฐ๋ฆฌ์˜ ์„œ๋น„์Šค)๊ฐ€ ์‚ฌ์šฉ์ž์˜ ์ ‘๊ทผ ๊ถŒํ•œ์„ ์œ„์ž„(Delegated Authorization)๋ฐ›์„ ์ˆ˜ ์žˆ๋Š” ํ‘œ์ค€ ํ”„๋กœํ† ์ฝœ.

 

 

 

 

Resource Owner = ์‚ฌ์šฉ์ž

Client = Resource Server์˜ ์ž์›์„ ์ด์š”ํ•˜๊ณ ์ž ํ•˜๋Š” ์„œ๋น„์Šค, ๋ณดํ†ต ๊ฐœ๋ฐœํ•˜๊ณ ์ž ํ•˜๋Š” ์„œ๋น„์Šค!

Authorization & Resource Server = ๋ณ„๊ฐœ ํ˜น์€ ํ•˜๋‚˜๋กœ ๊ตฌ์„ฑ ๊ฐ€๋Šฅํ•˜๋ฉฐ , ๊ตฌ๊ธ€ , ํŽ˜์ด์Šค๋ถ, ์นด์นด์˜ค์™€ ๊ฐ™์ด ๋ฆฌ์†Œ์Šค๋ฅผ ๊ฐ€์ง€๊ณ  ์žˆ๋Š” ์„œ๋ฒ„๋ฅผ ๋งํ•จ.

  • ๋กœ๊ทธ์ธ ์š”์ฒญ - ๊ตฌ๊ธ€๊ณ„์ •์œผ๋กœ ๋กœ๊ทธ์ธํ•˜๊ธฐ ๋“ฑ์ด ํ•ด๋‹น.
    1. ๊ตฌ๊ธ€(Authorization Server / Re - ์ค„์—ฌ์„œ As/Rs ๊ฐ€ ์ œ๊ณตํ•˜๋Š” URL์— client_id, redirect_uri , scope ๋“ฑ์˜ ๋งค๊ฐœ๋ณ€์ˆ˜๋ฅผ ํฌํ•จํ•˜์—ฌ ์ „๋‹ฌ
    2. Ex )

  • 3~4 ๋กœ๊ทธ์ธ ํŽ˜์ด์ง€ ์ œ๊ณต, ID / PW ์ œ๊ณต
    • ํด๋ผ์ด์–ธํŠธ๊ฐ€ ๋นŒ๋“œํ•œ ์ธ์ฆ ํŽ˜์ด์ง€๋กœ ์ด๋™ํ•œ ์‚ฌ์šฉ์ž๋Š” ์ œ๊ณต๋œ ๋กœ๊ทธ์ธ ํŽ˜์ด์ง€์—์„œ ID / PW๋กœ ์ธ์ฆ.
  • 5~6 Authorization Code ๋ฐœ๊ธ‰, Redirect URI๋กœ Redirect ์ฒ˜๋ฆฌ.
    • ์ธ์ฆ ์„ฑ๊ณต - As๊ฐ€ client๊ฐ€ ์ œ๊ณตํ•œ redirect_uri๋กœ ์‚ฌ์šฉ์ž๋ฅผ ๋˜์ง„๋‹ค. + (Authorization Code ํฌํ•จ)
      • ์ด Authorization Code๋Š” client๊ฐ€ Access Token์„ ํš๋“ํ•˜๊ธฐ ์œ„ํ•ด ์‚ฌ์šฉํ•˜๋Š” ์ž„์‹œ ์ฝ”๋“œ. (1๋ถ„์ •๋„ ์ˆ˜๋ช…)
  • 7~8 client๋Š” ~6๊นŒ์ง€์˜ ๊ฒฐ๊ณผ๋กœ ๋ฐ›์€ Authorization Code๋ฅผ As์— ์ „๋‹ฌํ•˜๊ณ  Access Token์„ ๋ฐ›๋Š”๋‹ค.
    • ๋ฐœ๊ธ‰๋ฐ›์€ Access Token์„ ์ €์žฅํ•˜๊ณ , Rs์˜ ๋ฆฌ์†Œ์Šค์— ์ ‘๊ทผ ํ•  ๋•Œ๋งˆ๋‹ค Access Token์„ ์‚ฌ์šฉํ•œ๋‹ค.
      • ํ† ํฐ ๊ฐ„์˜ ๊ตํ™˜์€ token ์—”๋“œํฌ์ธํŠธ์—์„œ ์ด๋ฃจ์–ด์ง„๋‹ค

  • 9 ๋กœ๊ทธ์ธ ์„ฑ๊ณต.
  • 10 ~ 13 ๋ฐœ๊ธ‰๋ฐ›์€ ์•ก์„ธ์Šค ํ† ํฐ์œผ๋กœ ๋ฆฌ์†Œ์Šค ์ ‘๊ทผ

 


Authorization Code์™€ Implicit Grant(์•”๋ฌต์  ์Šน์ธ)

 

๊ธ€ ์ถœ์ฒ˜ : https://hudi.blog/oauth-2.0/

Authorization Code๋ฅผ ๋ฐœ๊ธ‰ํ•˜์ง€ ์•Š๊ณ , ๊ณง๋ฐ”๋กœ Client์—๊ฒŒ Access Token์„ ๋ฐœ๊ธ‰ํ•ด์ค˜๋„ ๋˜์ง€ ์•Š์„๊นŒ? 
์™œ ๊ตณ์ด Access Token์„ ํš๋“ํ•˜๋Š” ๊ณผ์ •์— Authorization Code ๋ฐœ๊ธ‰ ๊ณผ์ •์ด ํ•„์š”ํ• ๊นŒ?

Redirect URI๋ฅผ ํ†ตํ•ด Authorization Code๋ฅผ ๋ฐœ๊ธ‰ํ•˜๋Š” ๊ณผ์ •์ด ์ƒ๋žต๋œ๋‹ค๋ฉด, 
Authorization Server๊ฐ€ Access Token์„ Client์— ์ „๋‹ฌํ•˜๊ธฐ ์œ„ํ•ด Redirect URI๋ฅผ ํ†ตํ•ด์•ผ ํ•œ๋‹ค.
์ด๋•Œ, Redirect URI๋ฅผ ํ†ตํ•ด ๋ฐ์ดํ„ฐ๋ฅผ ์ „๋‹ฌํ•  ๋ฐฉ๋ฒ•์€ URL ์ž์ฒด์— ๋ฐ์ดํ„ฐ๋ฅผ ์‹ค์–ด ์ „๋‹ฌํ•˜๋Š” ๋ฐฉ๋ฒ•๋ฐ–์— ์กด์žฌํ•˜์ง€ ์•Š๋Š”๋‹ค.
๋ธŒ๋ผ์šฐ์ €๋ฅผ ํ†ตํ•ด ๋ฐ์ดํ„ฐ๊ฐ€ ๊ณง๋ฐ”๋กœ ๋…ธ์ถœ๋˜๋Š” ๊ฒƒ ์ด๋‹ค.

ํ•˜์ง€๋งŒ, Access Token์€ ๋ฏผ๊ฐํ•œ ๋ฐ์ดํ„ฐ์ด๋‹ค. ์ด๋ ‡๊ฒŒ ์‰ฝ๊ฒŒ ๋…ธ์ถœ๋˜์–ด์„œ๋Š” ์•ˆ๋œ๋‹ค. 
์ด๋Ÿฐ ๋ณด์•ˆ ์‚ฌ๊ณ ๋ฅผ ๋ฐฉ์ง€ Authorization Code๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ ์ด๋‹ค.

Redirect URI๋ฅผ ํ”„๋ก ํŠธ์—”๋“œ ์ฃผ์†Œ๋กœ ์„ค์ •ํ•˜์—ฌ, Authorization Code๋ฅผ ํ”„๋ก ํŠธ์—”๋“œ๋กœ ์ „๋‹ฌํ•œ๋‹ค.
๊ทธ๋ฆฌ๊ณ  ์ด Authorization Code๋Š” ํ”„๋ก ํŠธ์—”๋“œ์—์„œ ๋ฐฑ์—”๋“œ๋กœ ์ „๋‹ฌ๋œ๋‹ค. 

์ฝ”๋“œ๋ฅผ ์ „๋‹ฌ๋ฐ›์€ 
๋ฐฑ์—”๋“œ๋Š” ๋น„๋กœ์†Œ Authorization Server์˜ token ์—”๋“œํฌ์ธํŠธ๋กœ ์š”์ฒญํ•˜์—ฌ Access Token์„ ๋ฐœ๊ธ‰ํ•œ๋‹ค.

์ด๋Ÿฐ ๊ณผ์ •์„ ๊ฑฐ์น˜๋ฉด Access Token์ด 
ํ•ญ์ƒ ์šฐ๋ฆฌ์˜ ์–ดํ”Œ๋ฆฌ์ผ€์ด์…˜๊ณผ OAuth ์„œ๋น„์Šค์˜ ๋ฐฑ์ฑ„๋„์„ ํ†ตํ•ด์„œ๋งŒ ์ „์†ก๋˜๊ธฐ ๋•Œ๋ฌธ์—
๊ณต๊ฒฉ์ž๊ฐ€ Access Token์„ ๊ฐ€๋กœ์ฑŒ ์ˆ˜ ์—†๊ฒŒ๋œ๋‹ค.

 

 

Implicit Grant โ”‚ ์•”๋ฌต์  ์Šน์ธ ๋ฐฉ์‹ - ์ด์ „์— javaScript์—์„œ ๋งŽ์ด ์ผ๋‹ค๊ณ  ํ•จ.

์œ„์™€ ๊ฐ™์€ ๋ฐฉ์‹ ์™ธ์—๋„ ์•”๋ฌต์  ์Šน์ธ ๋ฐฉ์‹์ด ์žˆ๋‹ค. ๊ถŒํ•œ ๋ถ€์—ฌ ์Šน์ธ ์ฝ”๋“œ ์—†์ด ๋ฐ”๋กœ Access Token์ด ๋ฐœ๊ธˆ๋˜๋Š” ๋ฐฉ์‹์ด๋ฉฐ, ๋งŒ๋ฃŒ๊ธฐ๊ฐ„์„ ์งง๊ฒŒ ์„ค์ •ํ•˜์—ฌ ๋ˆ„์ถœ ์œ„ํ—˜์„ ์ค„์ผ ํ•„์š”๊ฐ€ ์žˆ๋‹ค๊ณ  ํ•œ๋‹ค. - ๊ถŒ์žฅํ•˜์ง€ ์•Š๋Š” ๋ฐฉ์‹.

  • ๋˜ํ•œ Refresh Token์‚ฌ์šฉ์ด ๋ถˆ๊ฐ€๋Šฅํ•˜๋‹ค.
  • ์‘๋‹ต-ํšจ์œจ์ด ์ข‹์ง€๋งŒ Access token์ด URL๋กœ ์ „๋‹ฌ๋œ๋‹ค๋Š” ๋‹จ์ ์ด ์žˆ๋‹ค.

 

Authorization Code์—์„œ๋Š” ์‹ ๋ขฐ ํ•  ์ˆ˜ ์žˆ๋Š” back Channel์—์„œ ๋ฐ˜ํ™˜ ๋ฐ›์ง€๋งŒ, Implicit Grant๋ฐฉ์‹์—์„œ๋Š” Access token์ด URL์— ์ง์ ‘ ๋ฐ˜ํ™˜ (RedirectURL๋กœ ์ „๋‹ฌ๋˜๋Š” ๊ฒƒ.)

 

 

 

 

 

โŽˆ ๋.

 


 

 

 

 

์ฐธ์กฐ : https://www.youtube.com/watch?v=aEk-7RjBKwQ

์ฐธ์กฐ : https://hudi.blog/oauth-2.0/

 

OAuth 2.0 ๊ฐœ๋…๊ณผ ๋™์ž‘์›๋ฆฌ

2022๋…„ 07์›” 13์ผ์— ์ž‘์„ฑํ•œ ๊ธ€์„ ๋ณด์ถฉํ•˜์—ฌ ์ƒˆ๋กœ ํฌ์ŠคํŒ…ํ•œ ๊ธ€์ด๋‹ค. OAuth ๋“ฑ์žฅ ๋ฐฐ๊ฒฝ ์šฐ๋ฆฌ์˜ ์„œ๋น„์Šค๊ฐ€ ์‚ฌ์šฉ์ž๋ฅผ ๋Œ€์‹ ํ•˜์—ฌ ๊ตฌ๊ธ€์˜ ์บ˜๋ฆฐ๋”์— ์ผ์ •์„ ์ถ”๊ฐ€ํ•˜๊ฑฐ๋‚˜, ํŽ˜์ด์Šค๋ถ, ํŠธ์œ„ํ„ฐ์— ๊ธ€์„ ๋‚จ๊ธฐ๋Š” ๊ธฐ๋Šฅ์„

hudi.blog

์ฐธ์กฐ : https://blog.naver.com/mds_datasecurity/222182943542

 

OAuth 2.0 ๋™์ž‘ ๋ฐฉ์‹์˜ ์ดํ•ด

OAuth 2.0(Open Authorization 2.0, OAuth2)์€ ์ธ์ฆ์„ ์œ„ํ•œ ๊ฐœ๋ฐฉํ˜• ํ‘œ์ค€ ํ”„๋กœํ† ์ฝœ์ž…๋‹ˆ๋‹ค. ์ด ํ”„๋กœํ† ...

blog.naver.com

์ฐธ์กฐ : https://www.youtube.com/watch?v=ZEaJsa5dwNY

 

 

 

 

 

 

 

320x100
๋ฐ˜์‘ํ˜•
์ €์ž‘์žํ‘œ์‹œ (์ƒˆ์ฐฝ์—ด๋ฆผ)

'JAVA > SPRING' ์นดํ…Œ๊ณ ๋ฆฌ์˜ ๋‹ค๋ฅธ ๊ธ€

[Spring] @Valid , @Validated, BindingResult  (0) 2023.06.29
[Spring] Spring / Spring boot ํŠน์ง• ๊ฐ„๋‹จ ์ •๋ฆฌ.  (0) 2023.01.11
[NAVER maps ๊ธธ ์ฐพ๊ธฐ] springboot + webClient ๋กœ API ํ˜ธ์ถœ  (0) 2022.09.02
springBoot / mapstruct  (0) 2022.06.08
spring boot / H2 DB ์„ธํŒ…  (0) 2022.05.29
  1. Security  docs : 
  2. ์ž‘์—…ํ•œ ํ”„๋กœ์ ํŠธ ๋ฒ„์ „ 
  3. Spring Security์˜ ์ œ๊ณต ๊ธฐ๋Šฅ 
  4. ๋กœ๊ทธ์ธ์„ ํ†ตํ•œ ์ธ์ฆ ๊ตฌ์กฐ
  5. ์ž‘์—…ํ•œ ํ”„๋กœ์ ํŠธ์˜  SecurityConfig
  6. ์ธ์ฆ ํ•„ํ„ฐ๋Š”?
  7. ์ธ๊ฐ€ ํ•„ํ„ฐ๋Š”? 
  8. OAuth๋ฅผ ํ†ตํ•œ ๋กœ๊ทธ์ธ
  9. Authorization Code์™€ Implicit Grant(์•”๋ฌต์  ์Šน์ธ)
'JAVA/SPRING' ์นดํ…Œ๊ณ ๋ฆฌ์˜ ๋‹ค๋ฅธ ๊ธ€
  • [Spring] @Valid , @Validated, BindingResult
  • [Spring] Spring / Spring boot ํŠน์ง• ๊ฐ„๋‹จ ์ •๋ฆฌ.
  • [NAVER maps ๊ธธ ์ฐพ๊ธฐ] springboot + webClient ๋กœ API ํ˜ธ์ถœ
  • springBoot / mapstruct
girin_dev
girin_dev
๊ธฐ๋กํ•ฉ์‹œ๋‹ค.
girin_dev๊ธฐ๋กํ•ฉ์‹œ๋‹ค.
250x250
girin_dev
girin_dev
girin_dev

github.com/jaemanc


์ „์ฒด
์˜ค๋Š˜
์–ด์ œ
  • ๋ถ„๋ฅ˜ ์ „์ฒด๋ณด๊ธฐ (122)
    • ALGORITHM (23)
    • AWS (4)
    • Effective Java (4)
    • ERROR (12)
    • DB (11)
    • JAVA (23)
      • SPRING (10)
    • PYTHON (5)
      • TOY_PROJECT (1)
    • MOBILE (4)
    • SERVER (8)
    • TIPS (16)
    • WAS (2)
    • ์ƒˆ์‹น ์ผ๊ธฐ (5)
    • DATA (2)

๋ธ”๋กœ๊ทธ ๋ฉ”๋‰ด

  • ํ™ˆ
  • ํƒœ๊ทธ
  • ๋ฐฉ๋ช…๋ก

๊ณต์ง€์‚ฌํ•ญ

์ธ๊ธฐ ๊ธ€

ํƒœ๊ทธ

  • ํ”„๋กœ๊ทธ๋ž˜๋จธ์Šค
  • vertica
  • Flutter
  • java
  • ์ƒˆ์‹น
  • IntelliJ
  • offset
  • ๋ฐ”์งˆ ํ‚ค์šฐ๊ธฐ
  • ๊ฐ€์žฅ ํฐ ์ˆ˜
  • oracle cloud
  • docker
  • ๋ฐ”์งˆ
  • python3
  • oracle
  • Effective Java
  • springboot
  • spring boot
  • ๋ฐ”๋‘‘์ด
  • Chat GPT
  • ๋ฐ”์งˆ ํŽ˜์Šคํ† 
  • centos7
  • jwt
  • ๋‹ค์ด๋‚˜๋ฏน ํ”„๋กœ๊ทธ๋ž˜๋ฐ
  • dp
  • JAVA 11
  • lis
  • error
  • react-native
  • CentOS 8
  • querydsl

์ตœ๊ทผ ๋Œ“๊ธ€

์ตœ๊ทผ ๊ธ€

hELLO ยท Designed By ์ •์ƒ์šฐ.
girin_dev
[Spring] Spring Security setting๊ณผ ์—๋Ÿฌ ์‚ฌํ•ญ ์ˆ˜์ • ๊ณผ์ • ๊ธฐ๋ก๊ณผ OAuth2
์ƒ๋‹จ์œผ๋กœ

ํ‹ฐ์Šคํ† ๋ฆฌํˆด๋ฐ”

๋‹จ์ถ•ํ‚ค

๋‚ด ๋ธ”๋กœ๊ทธ

๋‚ด ๋ธ”๋กœ๊ทธ - ๊ด€๋ฆฌ์ž ํ™ˆ ์ „ํ™˜
Q
Q
์ƒˆ ๊ธ€ ์“ฐ๊ธฐ
W
W

๋ธ”๋กœ๊ทธ ๊ฒŒ์‹œ๊ธ€

๊ธ€ ์ˆ˜์ • (๊ถŒํ•œ ์žˆ๋Š” ๊ฒฝ์šฐ)
E
E
๋Œ“๊ธ€ ์˜์—ญ์œผ๋กœ ์ด๋™
C
C

๋ชจ๋“  ์˜์—ญ

์ด ํŽ˜์ด์ง€์˜ URL ๋ณต์‚ฌ
S
S
๋งจ ์œ„๋กœ ์ด๋™
T
T
ํ‹ฐ์Šคํ† ๋ฆฌ ํ™ˆ ์ด๋™
H
H
๋‹จ์ถ•ํ‚ค ์•ˆ๋‚ด
Shift + /
โ‡ง + /

* ๋‹จ์ถ•ํ‚ค๋Š” ํ•œ๊ธ€/์˜๋ฌธ ๋Œ€์†Œ๋ฌธ์ž๋กœ ์ด์šฉ ๊ฐ€๋Šฅํ•˜๋ฉฐ, ํ‹ฐ์Šคํ† ๋ฆฌ ๊ธฐ๋ณธ ๋„๋ฉ”์ธ์—์„œ๋งŒ ๋™์ž‘ํ•ฉ๋‹ˆ๋‹ค.