1220 - 1226


# 1220 - 1226

# 1221 - ์ „๋žตํŒจํ„ด๊ณผ ์ปค๋งจ๋“œํŒจํ„ด

์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ๊ตฌํ˜„ํ•˜์—ฌ ์‚ฌ์šฉํ•˜๋Š” ๋‘ ๊ฐ€์ง€ ํŒจํ„ด์ธ ์ „๋žต ํŒจํ„ด๊ณผ ์ปค๋งจ๋“œ ํŒจํ„ด์˜ ์ฐจ์ด

# ์ „๋žตํŒจํ„ด

์ „๋žต ํŒจํ„ด์€ ํ”„๋กœ๊ทธ๋žจ์ด ์ง„ํ–‰๋˜๋ฉด์„œ ์บก์Šํ™”๋œ ๋กœ์ง์„ ์„ ํƒํ•  ์ˆ˜ ์žˆ๊ฒŒ ํ•˜๋Š” ๋””์ž์ธ ํŒจํ„ด์ด๋‹ค. ๋กœ์ง ์‹คํ–‰์€ ์ธํ„ฐํŽ˜์ด์Šค์— ์˜์กด์„ ์‹œํ‚ค๊ณ  ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ๊ตฌํ˜„ํ•œ ๋กœ์ง๋“ค์„ ์ „๋‹ฌํ•ด์คŒ์œผ๋กœ์จ ๋ถ„๊ธฐ์ฒ˜๋ฆฌ ์—†์ด ์œ ์—ฐ์„œ์„ ๊ฐ–์ถœ ์ˆ˜๊ฐ€ ์žˆ๋‹ค.

  • ์˜ˆ์ œ ์ฝ”๋“œ
public class PeopleWithMovement {

    private Transportation transportation;

    public PeopleWithMovement(Transportation transportation) {
        this.transportation = transportation;
    }

    public void move(String start, String end) {
        transportation.move(start, end);
    }

    public void changeTransporation(Transportation transportation) {
        this.transportation = transportation;
    }
}

์ „๋žต์„ ํ–‰ํ•ญ ์ฃผ์ฒด์ธ Transporation์€ ์ธํ„ฐํŽ˜์ด์Šค๋กœ ๋ถ„๋ฆฌ๋˜์–ด ์บก์Šํ™” ๋˜์–ด ์žˆ๋‹ค.

public interface Transportation {

    void move(String start, String end);
}

// 
public class Bicycle implements Transportation {

    @Override
    public void move(String start, String end){
        System.out.println("์ถœ๋ฐœ์  : " + start + "์—์„œ ๋ชฉ์ ์ง€ : " + end + "๊นŒ์ง€ `์ž์ „๊ฑฐ`๋กœ ์ด๋™ํ•ฉ๋‹ˆ๋‹ค.");
    }
}

// 
public class Bus implements Transportation {

    @Override
    public void move(String start, String end){
        System.out.println("์ถœ๋ฐœ์  : " + start + "์—์„œ ๋ชฉ์ ์ง€ : " + end + "๊นŒ์ง€ `๋ฒ„์Šค`๋กœ ์ด๋™ํ•ฉ๋‹ˆ๋‹ค.");
    }
}

Transporation์„ ๊ตฌํ˜„ํ•˜์—ฌ Bus์™€ Bicycle ์ฝ”๋“œ ์ž‘์„ฑ

public class Main {

    public static void main(String[] args) {
        Bicycle bicycle = new Bicycle();
        Bus bus = new Bus();

        PeopleWithMovement whybeFirst = new PeopleWithMovement(bicycle);
        whybeFirst.move("์‹œ์ž‘์ ", "๋์ ");

        PeopleWithMovement whybeSecond = new PeopleWithMovement(bus);
        whybeSecond.move("์‹œ์ž‘์ ", "๋์ ");

        PeopleWithMovement whybeChangeMovement = new PeopleWithMovement(bicycle);
        whybeChangeMovement.move("์‹œ์ž‘์ ", "์ค‘๊ฐ„์ง€์ ");
        whybeChangeMovement.changeTransporation(bus);
        whybeChangeMovement.move("์ค‘๊ฐ„์ง€์ ", "๋์ ");
    }
}

์‹คํ–‰ ๊ฒฐ๊ณผ

์ถœ๋ฐœ์  : ์‹œ์ž‘์ ์—์„œ ๋ชฉ์ ์ง€ : ๋์ ๊นŒ์ง€ `์ž์ „๊ฑฐ`๋กœ ์ด๋™ํ•ฉ๋‹ˆ๋‹ค.
์ถœ๋ฐœ์  : ์‹œ์ž‘์ ์—์„œ ๋ชฉ์ ์ง€ : ๋์ ๊นŒ์ง€ `๋ฒ„์Šค`๋กœ ์ด๋™ํ•ฉ๋‹ˆ๋‹ค.
์ถœ๋ฐœ์  : ์‹œ์ž‘์ ์—์„œ ๋ชฉ์ ์ง€ : ์ค‘๊ฐ„์ง€์ ๊นŒ์ง€ `์ž์ „๊ฑฐ`๋กœ ์ด๋™ํ•ฉ๋‹ˆ๋‹ค.
์ถœ๋ฐœ์  : ์ค‘๊ฐ„์ง€์ ์—์„œ ๋ชฉ์ ์ง€ : ๋์ ๊นŒ์ง€ `๋ฒ„์Šค`๋กœ ์ด๋™ํ•ฉ๋‹ˆ๋‹ค.

# ์ปค๋งจ๋“œ ํŒจํ„ด

์ปค๋งจ๋“œ ํŒจํ„ด์€ ์š”์ฒญ์„ ํ™€๋กœ ์ฒ˜๋ฆฌํ•  ์ˆ˜ ์žˆ๋„๋ก ์š”์ฒญ์„ ์ˆ˜ํ–‰ํ•˜๋Š” ์—ฌ๋Ÿฌ ์ธ์ž๋ฅผ ํ•จ๊ป˜ ํŒจํ‚ค์ง•ํ•˜์—ฌ ๋‚˜์ค‘์— ์ฒ˜๋ฆฌํ•  ์ˆ˜ ์žˆ๋„๋ก ๋งŒ๋“ค์–ด์ฃผ๋Š” ํ–‰๋™ ์ค‘์‹ฌ ๋””์ž์ธ ํŒจํ„ด์ด๋‹ค. ์ปค๋งจ๋“œ๋ฅผ ์‹คํ–‰์‹œํ‚ค๋Š” ๊ฐ์ฒด๋Š” ์ปค๋งจ๋“œ ๋‚ด๋ถ€์˜ ์š”์†Œ์— ๋Œ€ํ•ด์„œ ์ˆจ๊น€์œผ๋กœ์จ ์ฝ”๋“œ์˜ ์œ ์—ฐ์„ฑ์„ ๊ฐ€์งˆ ์ˆ˜ ์žˆ๋‹ค.

  • ์˜ˆ์ œ ์ฝ”๋“œ
public class PeopleWithLottery {

    private List<LotteryCommand> lotteryCommands;

    public PeopleWithLottery(List<LotteryCommand> lotteryCommands) {
        this.lotteryCommands = lotteryCommands;
    }

    public void addLotteryCommand(LotteryCommand lotteryCommand) {
        lotteryCommands.add(lotteryCommand);
    }

    public void scratchAllLottery() {
        for (int i = 0; i < lotteryCommands.size(); i++) {
            LotteryCommand lotteryCommand = lotteryCommands.get(i);
            lotteryCommand.scratch();
        }
        //์ดˆ๊ธฐํ™”
        lotteryCommands = new LinkedList<>();
    }
}

public interface LotteryCommand {
    void scratch();
}

public class InstantScratch implements LotteryCommand {
    
    private InstantLottery instantLottery;
    private Account account;

    public InstantScratch(InstantLottery instantLottery, Account account) {
        this.instantLottery = instantLottery;
        this.account = Account;
    }

    @Override
    public void scratch() {
      //instantLottery์˜ ๋‹น์ฒจ์„ ํ™•์ธํ•˜๊ณ  account์— ๋ˆ์„ ์ง‘์–ด ๋„ฃ๋Š” ๋กœ์ง
    }
}

public class InstantLottery {
    
    private boolean win;

    public InstantLottery(boolean win) {
        this.win = win;
    }

    public boolean isWin() {
        return win;
    }
}

public class Account {

    private int balance;

    public void putMoney(int money) {
        balance += money;
    }
}
public class Main {
    public static void main(String[] args) {
        PeopleWithLottery whybe = new PeopleWithLottery(new LinkedList<>());
        Account ์™€์ด๋น„ํ†ต์žฅ = new Account();
        
        //์ฆ‰์„๋ณต๊ถŒ ๊ตฌ์ž…
        for (int i = 0; i < 10; i++) {
            //์ฆ‰์„๋ณต๊ถŒ ์ƒ์„ฑ ๋กœ์ง 
            InstantLottery instantLottery = new InstantLottery(๋‹น์ฒจ์—ฌ๋ถ€);
            //์ฆ‰์„๋ณต๊ถŒ๊ธ๊ธฐํ–‰์œ„ ๊ฐ์ฒด ์ƒ์„ฑ ๋ฐ ์ปค๋งจ๋“œ ๋ชฉ๋ก์— ์ถ”๊ฐ€
            InstantScratch ์ฆ‰์„๋ณต๊ถŒ๊ธ๊ธฐ์ปค๋งจ๋“œ = new InstantScratch(์ฆ‰์„๋ณต๊ถŒ, ์™€์ด๋น„ํ†ต์žฅ);
            whybe.addLotteryCommand(์ฆ‰์„๋ณต๊ถŒ๊ธ๊ธฐ์ปค๋งจ๋“œ);
        }
        
        whybe.scratchAllLotery();
    }
}

๋‹น์ฒจ์„ ํ™•์ธํ•˜๊ณ  ํ†ต์žฅ์— ๋ˆ์„ ์ถ”๊ฐ€ํ•˜๋Š” ๊ฒƒ์„ ์บก์Šํ™”ํ•˜๊ณ , ๊ธ๋Š” ํ–‰์œ„๋ฅผ ํ˜ธ์ถœํ•˜๋Š” ๊ฐ์ฒด(People), ๋ช…๋ น์„ ๋‹ด์žฅํ•˜๋Š” ๊ฐ์ฒด(LotteryCommand) ๊ทธ๋ฆฌ๊ณ  ๋ช…๋ น ์ˆ˜ํ–‰์œผ๋กœ ์ธํ•˜์—ฌ ์˜ํ–ฅ์„ ๋ฐ›๋Š” ๊ฐ์ฒด(MyAccount)๊ฐ€ ๋ชจ๋‘ ๋‹ค๋ฅด๊ฒŒ ๊ตฌ์„ฑ.

public class NumberScratch implements LotteryCommand {

    private Set<Integer> winners;
    private NumberLottery numberLottery;
    private Account account;

    public NumberScratch(Set<Integer> winners, NumberLottery numberLottery, Account account) {
        this.winners = winners;
        this.numberLottery = numberLottery;
        this.account = account;
    }

    @Override
    public void scratch() {
        // winners์™€ numberLottery๋ฅผ ๋น„๊ตํ•˜์—ฌ ๋‹น์ฒจ๊ธˆ์„ ๊ณ„์‚ฐํ•˜๊ณ  
        // account์— ํ•ด๋‹น ๊ธˆ์•ก์„ ์ž…๊ธˆํ•˜๋Š” ๋กœ์ง
    }   
}

public class NumberLottery {
    
    private Set<Integer> numbers;

    public NumberLottery(Set<Integer> numbers) {
        this.numbers = numbers;
    }

    public int rank(Set<Integer> winners) {
        // ๋‹น์ฒจ ๋ฒˆํ˜ธ์™€ ๋น„๊ตํ•˜์—ฌ ์ž์‹ ์˜ ๋“ฑ์ˆ˜๋ฅผ ๊ณ„์‚ฐํ•˜๋Š” ๋กœ์ง
    }
}
public class Main {
    public static void main(String[] args) {
        PeopleWithLottery whybe = new PeopleWithLottery(new LinkedList<>());
        Account ์˜์ดํ†ต์žฅ = new Account();
        
        //์ฆ‰์„๋ณต๊ถŒ ๊ตฌ์ž…
        for (int i = 0; i < 10; i++) {
            //๋ฒˆํ˜ธ์‹ ๋ณต๊ถŒ ์ƒ์„ฑ ๋กœ์ง 
            NumberLottery ๋ฒˆํ˜ธ์‹๋ณต๊ถŒ = new NumberLottery(์„ ํƒํ•œ ๋ฒˆํ˜ธ);
            //๋ฒˆํ˜ธ์‹๋ณต๊ถŒ๊ธ๊ธฐํ–‰์œ„ ๊ฐ์ฒด ์ƒ์„ฑ ๋ฐ ์ปค๋งจ๋“œ ๋ชฉ๋ก์— ์ถ”๊ฐ€
            NumberScratch ๋ฒˆํ˜ธ์‹๋ณต๊ถŒ๊ธ๊ธฐ์ปค๋งจ๋“œ = new NumberScratch(์ด๋ฒˆ์ฃผ ๋‹น์ฒจ๋ฒˆํ˜ธ, ๋ฒˆํ˜ธ์‹๋ณต๊ถŒ, ์˜์ดํ†ต์žฅ);
            whybe.addLotteryCommand(๋ฒˆํ˜ธ์‹๋ณต๊ถŒ๊ธ๊ธฐ์ปค๋งจ๋“œ);
        }

        whybe.scratchAllLottery();
    }
}

ํ–‰์œ„๋ฅผ ์บก์Šํ™”ํ•˜์—ฌ ๋ณต๊ถŒ์„ ๊ธ๋Š” ํ–‰์œ„๋ฅผ ํ˜ธ์ถœํ•˜๋Š” ๊ฐ์ฒด์ธ PeopleWithLottery์™€ ๋ช…๋ น ์ˆ˜ํ–‰์œผ๋กœ ์ธํ•˜์—ฌ ์˜ํ–ฅ์„ ๋ฐ›๋Š” ๊ฐ์ฒด์— ํฐ ๋ณ€ํ™”๋ฅผ ๊ฑฐ์น˜์ง€ ์•Š๋„๋ก ๋งŒ๋“ฌ

# ์ „๋žตํŒจํ„ด๊ณผ ์ปค๋งจ๋“œํŒจํ„ด์˜ ์ฐจ์ด

์ „๋žต ํŒจํ„ด์€ ์–ด๋–ป๊ฒŒ๋ผ๋Š” ์ธก๋ฉด์— ์ง‘์ค‘. ์ปค๋งจ๋“œ ํŒจํ„ด์€ ๋ฌด์—‡์— ์ดˆ์ 


# 1224 - Multipart

# Multipart๋ž€?

  • ์›น ํด๋ผ์ด์–ธํŠธ๊ฐ€ ์š”์ฒญ์„ ๋ณด๋‚ผ ๋•Œ, http ํ”„๋กœํ† ์ฝœ์˜ ๋ฐ”๋”” ๋ถ€๋ถ„์— ๋ฐ์ดํ„ฐ๋ฅผ ์—ฌ๋Ÿฌ ๋ถ€๋ถ„์œผ๋กœ ๋‚˜๋ˆ ์„œ ๋ณด๋‚ด๋Š” ๊ฒƒ. ์›น ํด๋ผ์ด์–ธํŠธ๊ฐ€ ์„œ๋ฒ„์—๊ฒŒ ํŒŒ์ด์„ ์—…๋กœ๋“œ ํ•  ๋•Œ, http ํ”„๋กœํ† ์ฝœ์˜ ๋ฐ”๋”” ๋ถ€๋ถ„์— ํŒŒ์ผ์ •๋ณด๋ฅผ ๋‹ด์•„์„œ ์ „์†ก์„ ํ•˜๋Š”๋ฐ, ํŒŒ์ผ์„ ํ•œ๋ฒˆ์— ์—ฌ๋Ÿฌ๊ฐœ ์ „์†ก์„ ํ•˜๋ฉด body ๋ถ€๋ถ„์— ํŒŒ์ผ์ด ์—ฌ๋Ÿฌ๊ฐœ์˜ ๋ถ€๋ถ„์œผ๋กค ์—ฐ๊ฒฐ๋˜์–ด ์ „์†ก๋œ๋‹ค. ์ด๋ ‡๊ฒŒ ์—ฌ๋Ÿฌ ๋ถ€๋ถ„์œผ๋กœ ๋‚˜๋‰˜์–ด์„œ ์ „์†ก๋˜๋Š” ๊ฒƒ์„ Multipart data๋ผ๊ณ  ํ•œ๋‹ค.
  • ๋ณดํ†ต ํŒŒ์ผ์„ ์ „์†กํ•  ๋•Œ ์‚ฌ์šฉํ•œ๋‹ค.

# HttpServletRequest๋Š” ํŒŒ์ผ ์—…๋กœ๋“œ๋ฅผ ์ง€์›ํ•˜์ง€ ์•Š๋Š”๋‹ค.

  • HttpServletRequest๋Š” ์›น ํด๋ผ์ด์–ธํŠธ๊ฐ€ ์ „๋‹ฌํ•˜๋Š” Multipart ๋ฐ์ดํ„ฐ๋ฅผ ์‰ฝ๊ฒŒ ์ฒ˜๋ฆฌํ•˜๋Š” ๋ฉ”์†Œ๋“œ๋ฅผ ์ œ๊ณตํ•˜์ง€ ์•Š๋Š”๋‹ค. (HttpServletRequest๋Š” http ํ”„๋กœํ† ์ฝœ์˜ body ๋ถ€๋ถ„์„ ์ฝ์–ด๋“ค์ด๋Š” input ์ŠคํŠธ๋ฆผ์•ˆ์„ ์ง€์›, ์‚ฌ์šฉ์ž๋Š” ์ด๋Ÿฐ input ์ŠคํŠธ๋ฆผ์„ ์ด์šฉํ•ด์„œ Multipart๋ถ€๋ถ„์„ ์ž˜ ๋‚˜๋ˆ„์–ด์„œ ์‚ฌ์šฉํ•ด์•ผํ•œ๋‹ค. ๋ณดํ†ต์€ ์ง์ ‘ ๊ตฌํ˜„ํ•˜์ง€ ์•Š๊ณ  ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ์‚ฌ์šฉ)
  • ์„œ๋ธ”๋ฆฟ์—์„œ ํŒŒ์ผ ์—…๋กœ๋“œ๋ฅผ ์ฒ˜๋ฆฌํ•˜๋ ค๋ฉด ๋ณ„๋„์˜ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ์‚ฌ์šฉํ•ด์•ผ ํ•œ๋‹ค. ๋Œ€ํ‘œ์ ์ธ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๊ฐ€ ์•„ํŒŒ์น˜ ์žฌ๋‹จ์˜ commons-filedupload์ด๋‹ค.

# Spring MVC์—์„œ์˜ ํŒŒ์ผ ์—…๋กœ๋“œ

Spring MVC์—์„œ ํŒŒ์ผ ์—…๋กœ๋“œ ํ•˜๋ ค๋ฉด ๋ช‡๊ฐ€์ง€ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์™€ ์„ค์ •์„ ์ถ”๊ฐ€

  • commons-fileupload, commons-io ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ์ถ”๊ฐ€
  • MultipartResolver Bean ์ถ”๊ฐ€
  • DispatcherServlet์€ ์ค€๋น„๊ณผ์ •์—์„œ "multipart/form-data"๊ฐ€ ์š”์ฒญ์œผ๋กœ ์˜ฌ ๊ฒฝ์šฐ MultipartResolver๋ฅผ ์‚ฌ์šฉํ•œ๋‹ค. (MultipartFile ๋งค๊ฐœ๋ณ€์ˆ˜๋ฅผ ์‚ฌ์šฉํ•˜๊ธฐ ์œ„ํ•ด์„œ๋Š” MultipartResolver Bean์ด ๋“ฑ๋ก๋˜์–ด์žˆ์–ด์•ผ ํ•œ๋‹ค.)
@Bean
public MultipartResolver multipartResolver(){
    org.springframework.web.multipart.commons.CommonsMultipartResolver multipartResolver = new 
    org.springframework.web.multipart.commons.CommonsMultipartResolver();
    
    multipartResolver.setMaxUplaodSize(10485760); //1024 * 1024 * 10 (์ตœ๋Œ€ 10MB)
    return multipartResolver;
}
package org.springframework.boot.autoconfigure.web.servlet;

@Configuration(proxyBeanMethods = false)
@ConditionalOnClass({ Servlet.class, StandardServletMultipartResolver.class, MultipartConfigElement.class })
@ConditionalOnProperty(prefix = "spring.servlet.multipart", name = "enabled", matchIfMissing = true)
@ConditionalOnWebApplication(type = Type.SERVLET)
@EnableConfigurationProperties(MultipartProperties.class)
public class MultipartAutoConfiguration {

	private final MultipartProperties multipartProperties;

	public MultipartAutoConfiguration(MultipartProperties multipartProperties) {
		this.multipartProperties = multipartProperties;
	}

	@Bean
	@ConditionalOnMissingBean({ MultipartConfigElement.class, CommonsMultipartResolver.class })
	public MultipartConfigElement multipartConfigElement() {
		return this.multipartProperties.createMultipartConfig();
	}

	@Bean(name = DispatcherServlet.MULTIPART_RESOLVER_BEAN_NAME)
	@ConditionalOnMissingBean(MultipartResolver.class)
	public StandardServletMultipartResolver multipartResolver() {
		StandardServletMultipartResolver multipartResolver = new StandardServletMultipartResolver();
		multipartResolver.setResolveLazily(this.multipartProperties.isResolveLazily());
		return multipartResolver;
	}

}

MultipartResolver๋Š” SpringMVC์—์„œ ๊ธฐ๋ณธ์ ์œผ๋กœ ๋“ฑ๋กํ•ด์ฃผ์ง€ ์•Š์ง€๋งŒ, SpringBoot์—์„œ๋Š” ์œ„์™€ ๊ฐ™์ด MultipartAutoConfigurationํด๋ž˜์Šค์—์„œ MultipartResolver Bean์ด ๋“ฑ๋ก๋˜์–ด์žˆ์ง€ ์•Š๋Š” ๊ฒฝ์šฐ ์ž๋™์œผ๋กœ Bean์„ ๋“ฑ๋กํ•ด์ค€๋‹ค. ๋”ฐ๋ผ์„œ SpringBoot์—์„œ๋Š” ๋ณ„๋„์˜ ์„ค์ •์—†์ด MultipartFile ๋งค๊ฐœ๋ณ€์ˆ˜๋ฅผ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค.


# 1225 - @AuthenticationPrincipal

์Šคํ”„๋ง ์‹œํ๋ฆฌํ‹ฐ๋Š” SecurityContext์— ์ธ์ฆ๋œ Authentication ๊ฐ์ฒด๋ฅผ ๋„ฃ์–ด๋‘๊ณ  ํ˜„์žฌ ์Šค๋ ˆ๋“œ ๋‚ด์—์„œ ๊ณต์œ ๋˜๋„๋ก ๊ด€๋ฆฌํ•˜๊ณ  ์žˆ๋‹ค. ๋ณดํ†ต์€ ์ธ์ฆ ์ดํ›„ @AuthenticationPrincipal ์–ด๋…ธํ…Œ์ด์…˜์„ ํ†ตํ•ด UserDetails ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ๊ตฌํ˜„ํ•œ ์œ ์ € ๊ฐ์ฒด๋ฅผ ์ฃผ์ž…ํ•  ๋•Œ ์‚ฌ์šฉํ•˜๋Š” ํŽธ์ด๋‹ค.

๊ด€๋ จ ํด๋ž˜์Šค์™€ ์ธํ„ฐํŽ˜์ด์Šค

  • SecurityContext : ์ธํ„ฐํŽ˜์ด์Šค. Security Context ์ธํ„ฐํŽ˜์ด์Šค. Authentication์— ๋Œ€ํ•œ ์ ‘๊ทผ getter๋ฅผ ์ •์˜ํ•ด ๋†“์•˜๋‹ค.
  • SecurityContextImpl : ํด๋ž˜์Šค. SecurityContext ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ๊ตฌํ˜„ํ•œ ๊ฐ์ฒด Authentication ๊ฐ์ฒด์— ๋Œ€ํ•œ getter/setter๋ฅผ ์ •์˜ํ•ด ๋†“์€ ๊ฐ์ฒด. ํ•ด๋‹น ๊ตฌํ˜„์ฒด๋ฅผ ํ†ตํ•ด ๋‚ด๋ถ€์ ์œผ๋กœ ํ˜„์žฌ ์Šค๋ ˆ๋“œ์˜ Security Context๋ฅผ ์ƒ์„ฑํ•˜์—ฌ ์ธ์ฆ ํ›„ Authentication ๊ฐ์ฒด๋ฅผ ๋„ฃ์–ด๋†“๋Š” ์—ญํ• ์„ ํ•œ๋‹ค.
  • Authentication : ์ธ์ฆ ์ •๋ณด์— ๋Œ€ํ•œ ๋ถ€๋ถ„์„ ์ •์˜ํ•ด๋†“์€ ์ธํ„ฐํŽ˜์ด์Šค. Principal๊ณผ ๊ฐ™์€ Credentials, Authorities ์— ๋Œ€ํ•œ ์ •์˜๊ฐ€ ๋˜์–ด์žˆ๋‹ค. ์—ฌ๋Ÿฌ ๊ตฌํ˜„์ฒด๊ฐ€ ์žˆ๋‹ค. (ex. UsernamePasswordAuthenticationToken)
    • Principal์˜ ์˜๋ฏธ๋Š” "์ธ์ฆ๋˜๋Š” ์ฃผ์ฒด์˜ ID"
    • Credentials์€ "์ฃผ์ฒด๊ฐ€ ์ •ํ™•ํ•œ์ง€ ์ฆ๋ช…ํ•˜๋Š” ๊ฒƒ"
    • Authorities๋Š” "๊ถŒํ•œ"
  • UserDetails : ์‚ฌ์šฉ์ž ์ •๋ณด๋ฅผ ๊ฐ€์ง€๋Š” ์ธํ„ฐํŽ˜์ด์Šค. ์ด๋ฅผ ๊ตฌํ˜„ํ•˜์—ฌ ์‹ค์ œ ๋กœ๊ทธ์ธ์— ์‚ฌ์šฉํ•  ํด๋ž˜์Šค๋ฅผ ๋งŒ๋“ค๋ฉด ๋˜๊ณ , ์Šคํ”„๋ง ์‹œํ๋ฆฌํ‹ฐ ๋‚ด๋ถ€์ ์œผ๋กœ ์ง์ ‘ ์‚ฌ์šฉํ•˜์ง€๋Š” ์•Š๊ณ  Authentication์œผ๋กœ ์บก์Šํ™”ํ•˜์—ฌ ์ €์žฅ๋œ๋‹ค. ๋”ฐ๋ผ์„œ UserDetails๊ตฌํ˜„์ฒด์˜ ์ •๋ณด๋Š” Spring Security Context์— ์ €์žฅ๋œ Authentication ๊ฐ์ฒด๊ฐ€ ๊ฐ€์ ธ๊ฐ„๋‹ค.
  • HandlerMethodArgumentResolver : ์ธํ„ฐํŽ˜์ด์Šค. ํŠน์ • ์ ๋žต์— ๋”ฐ๋ผ ํ•œ request์—์„œ ๋„˜์–ด์˜จ ์ธ์ž๋“ค์„ ๋ฉ”์†Œ๋“œ ํŒŒ๋ผ๋ฏธํ„ฐ๋กœ ํ•ด์„ํ•  ์ˆ˜ ์žˆ๋„๋ก ๋„์™€์คŒ
  • AuthenticationPrincipalArgumentResolver : ์Šคํ”„๋ง ์‹œํ๋ฆฌํ‹ฐ์—์„œ HandlerMethodArgumentResolver๋ฅผ ๊ตฌํ˜„ํ•œ ๊ตฌํ˜„์ฒด๋กœ @AuthenticationPrincipal ์–ด๋…ธํ…Œ์ด์…˜์ด ์‹ค์ œ๋กœ ์‚ฌ์šฉ๋˜๋Š” ๋ถ€๋ถ„.

# ๋™์ž‘ ์›๋ฆฌ

ํ˜„์žฌ ์Šค๋ ˆ๋“œ๊ฐ€ ์„ธ์…˜์„ ๋ฌผ๊ณ  ์žˆ๋‹ค๋ฉด ์ด๋ฏธ SecurityContextHolder์˜ getContext() ๋ฉ”์†Œ๋“œ๋ฅผ ํ†ตํ•ด SecurityContext ๊ฐ์ฒด๋ฅผ ์–ป๊ณ  ๊ทธ ์•ˆ์˜ getAuthentication() ๋ฉ”์†Œ๋“œ๋ฅผ ํ†ตํ•ด Authentication (์ธ์ฆ๊ฐ์ฒด)๋ฅผ ์–ป์„ ์ˆ˜ ์žˆ๋‹ค.

@PostMapping(value = "/logout", 
             consumes = MediaTypes.HAL_JSON_VALUE, 
             produces = MediaTypes.HAL_JSON_VALUE)
public Object logout(@AuthenticationPrincipal SecurityUser securityUser, 
                     @RequestHeader("Authorization") String authorization) throws Exception {
    // ์ปจํŠธ๋กค๋Ÿฌ ๋กœ์ง
}

Spring Security๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ๊ฒฝ์šฐ, AuthenticationPrincipalArgumentResolver ํด๋ž˜์Šค๋ฅผ ํ™œ์šฉํ•˜์—ฌ resolveArgument ๋ฉ”์†Œ๋“œ๋ฅผ ๊ตฌํ˜„ํ•˜๊ณ  SecurityContext์— ์ €์žฅ๋œ ์ธ์ฆ๊ฐ์ฒด๋ฅผ ๊ธฐ๋ฐ˜์œผ๋กœ Authentication ๊ฐ์ฒด๋ฅผ ๊บผ๋‚ด์˜ค๊ฒŒ ๋œ๋‹ค.

public final class AuthenticationPrincipalArgumentResolver implements HandlerMethodArgumentResolver {

	private ExpressionParser parser = new SpelExpressionParser();

	private BeanResolver beanResolver;

	@Override
	public boolean supportsParameter(MethodParameter parameter) {
		return findMethodAnnotation(AuthenticationPrincipal.class, parameter) != null;
	}

	@Override
	public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer,
			NativeWebRequest webRequest, WebDataBinderFactory binderFactory) {
            
        // Security Context์—์„œ ๊บผ๋‚ด์š˜ ๊ฐ์ฒด๋ฅผ ๊ธฐ๋ฐ˜์œผ๋กœ ํ•œ๋‹ค.
		Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
		if (authentication == null) {
			return null;
		}

        // ์ธ์ฆ๊ฐ์ฒด์—์„œ Principal์„ ๊บผ๋‚ธ๋‹ค.
		Object principal = authentication.getPrincipal();

        // ํ•ด๋‹น ์–ด๋…ธํ…Œ์ด์…˜์ด ๋ถ™์€ ํŒŒ๋ผ๋ฏธํ„ฐ๊ฐ€ ์กด์žฌํ•˜๋Š”์ง€ ์ฐพ๋Š”๋‹ค.
		AuthenticationPrincipal annotation = findMethodAnnotation(AuthenticationPrincipal.class, parameter);
		String expressionToParse = annotation.expression();
		if (StringUtils.hasLength(expressionToParse)) {
			StandardEvaluationContext context = new StandardEvaluationContext();
			context.setRootObject(principal);
			context.setVariable("this", principal);
			context.setBeanResolver(this.beanResolver);
			Expression expression = this.parser.parseExpression(expressionToParse);
			principal = expression.getValue(context);
		}
		if (principal != null && !parameter.getParameterType().isAssignableFrom(principal.getClass())) {
			if (annotation.errorOnInvalidType()) {
				throw new ClassCastException(principal + " is not assignable to " + parameter.getParameterType());
			}
			return null;
		}

        // principal ๋ฆฌํ„ด
		return principal;
	}

	public void setBeanResolver(BeanResolver beanResolver) {
		this.beanResolver = beanResolver;
	}

	private <T extends Annotation> T findMethodAnnotation(Class<T> annotationClass, MethodParameter parameter) {
		T annotation = parameter.getParameterAnnotation(annotationClass);
		if (annotation != null) {
			return annotation;
		}
		Annotation[] annotationsToSearch = parameter.getParameterAnnotations();
		for (Annotation toSearch : annotationsToSearch) {
			annotation = AnnotationUtils.findAnnotation(toSearch.annotationType(), annotationClass);
			if (annotation != null) {
				return annotation;
			}
		}
		return null;
	}

}

์ปจํŠธ๋กค๋Ÿฌ์˜ ํŒŒ๋ผ๋ฏธํ„ฐ๋งˆ๋‹ค ํ•ด๋‹น resolveArgument ๋ฉ”์†Œ๋“œ๊ฐ€ ์‹คํ–‰๋œ๋‹ค.

Last update: September 13, 2022 21:44
Contributors: ahnjs