online user - woowa-turkey/miniprojects-2019 GitHub Wiki

์ฒ˜์Œ ์ƒ๊ฐ

DB์— ๋กœ๊ทธ์ธ ์‹œ๊ฐ„๊ณผ ๋กœ๊ทธ์•„์›ƒ ์‹œ๊ฐ„์„ ์ €์žฅ

๋กœ๊ทธ์ธ ์‹œ๊ฐ„์ด ๋กœ๊ทธ์•„์›ƒ ์‹œ๊ฐ„ ๋น ๋ฅด๋ฉด โ†’ ๋กœ๊ทธ์ธ

๋กœ๊ทธ์ธ ์‹œ๊ฐ„์ด ๋กœ๊ทธ์•„์›ƒ ์‹œ๊ฐ„ ๋Šฆ์œผ๋ฉด โ†’ ๋กœ๊ทธ์•„์›ƒ

์ดˆ๊ธฐ User ์ƒ์„ฑ์‹œ์—๋Š” ์‹œ๊ฐ„์„ ๊ฐ™๊ฒŒ ํ•ด๋†“๊ณ  ์‹œ๊ฐ„์ด ๊ฐ™์œผ๋ฉด ๋กœ๊ทธ์•„์›ƒ์œผ๋กœ ์ฒ˜๋ฆฌ

public boolean isLogin() {
    return loginAt.isAfter(logoutAt);
}

๋ฌธ์ œ์  1. ๋กœ๊ทธ์•„์›ƒ์„ ์•ˆํ•˜๊ณ  ๋ธŒ๋ผ์šฐ์ €๋ฅผ ์ข…๋ฃŒํ•˜๊ฑฐ๋‚˜ ๋‹ค๋ฅธ ํŽ˜์ด์ง€๋กœ ๋„˜์–ด๊ฐ€๋Š” ๊ฒฝ์šฐ

๋ฐฉ๋ฒ• 1. js์— ์žˆ๋Š” unload ์ด๋ฒคํŠธ ํ™œ์šฉํ•ด์„œ "๋‹ค๋ฅธ ํŽ˜์ด์ง€๋กœ ๋„˜์–ด๊ฐˆ๋•Œ ๋กœ๊ทธ์•„์›ƒํ•˜์‹œ๊ฒ ์Šต๋‹ˆ๊นŒ?"๋ผ๋Š” ๋ฉ”์„ธ์ง€๋ฅผ ๋ณด์—ฌ์ฃผ๋ฉด ๋˜๊ฒ ๋‹ค.

window,document,resources๊ฐ€ unload๋  ๋•Œ ๋ฐœ์ƒํ•˜๋Š” ์ด๋ฒคํŠธ ์ˆœ์„œ : beforeunload โ†’ pagehide โ†’ unload

  1. beforeunload

    img

    ์ด ์ด๋ฒคํŠธ๊ฐ€ ๋ฐœ์ƒํ•  ๋‹น์‹œ์—๋Š” ํ™”๋ฉด์€ ์—ฌ์ „ํžˆ ํ‘œ์‹œ๋˜๊ณ  ์ทจ์†Œํ•  ์ˆ˜ ์žˆ๋‹ค.

    • ์‚ฌ์šฉ๋ฐฉ๋ฒ•
    // beforeunload event ์‚ฌ์šฉ๋ฐฉ๋ฒ•
    1.
    window.addEventListener('beforeunload', function(event) { 
      // ๊ธฐ๋ณธ์€ ์•„๋ž˜ ๋ฉ”์„œ๋“œ๋ฅผ ์ด์šฉํ•ด์„œ ํ™•์ธ์ฐฝ์„ ๋„์šด๋‹ค.
      event.preventDefault();
      // Chrome ํฌ๋กฌ์€ returneValue์— ๊ฐ’์„ ๋„ฃ์–ด์ค˜์•ผ ํ•œ๋‹ค. ์˜๋ฏธ๋Š” ์—†๋‹ค.
      event.returnValue = '';
      // ์ผ๋ถ€ ๋ธŒ๋ผ์šฐ์ €๋Š” string returnํ•ด์•ผํ•œ๋‹ค.
    });
    
    2.
    window.onbeforeunload = function(event) { 
      // ๊ธฐ๋ณธ์€ ์•„๋ž˜ ๋ฉ”์„œ๋“œ๋ฅผ ์ด์šฉํ•ด์„œ ํ™•์ธ์ฐฝ์„ ๋„์šด๋‹ค.
      event.preventDefault();
      // Chrome ํฌ๋กฌ์€ returneValue์— ๊ฐ’์„ ๋„ฃ์–ด์ค˜์•ผ ํ•œ๋‹ค. ์˜๋ฏธ๋Š” ์—†๋‹ค.
      event.returnValue = '';
      // ์ผ๋ถ€ ๋ธŒ๋ผ์šฐ์ €๋Š” string returnํ•ด์•ผํ•œ๋‹ค.
    };
    

    ๋‹จ์ 

    • IE์ œ์™ธํ•˜๊ณ ๋Š” ๋ฉ”์‹œ์ง€ ์ปค์Šคํ…€์ด ๋ถˆ๊ฐ€๋Šฅ
    • ๋กœ๊ทธ์•„์›ƒ์ด ์ •์žฅ์ ์œผ๋กœ ๋˜์—ˆ๋Š”์ง€ ์•Œ ์ˆ˜ ์—†๋‹ค.
    • ์ƒˆ๋กœ๊ณ ์นจํ•˜๊ฑฐ๋‚˜ ๋‹ค๋ฅธ ํŽ˜์ด์ง€๋กœ ๋„˜์–ด๊ฐˆ ๋•Œ๋„ ๋ฌด์กฐ๊ฑด ์‹คํ–‰๋œ๋‹ค.

  1. pagehide

    ์ทจ์†Œ๊ฐ€ ๋ถˆ๊ฐ€๋Šฅํ•˜๋‹ค.

    ์‚ฌ์šฉ์ž์—๊ฒŒ ๋ญ”๊ฐ€ ํ‘œ์‹œ๋ฅผ ํ•ด์ค„ ์ˆ˜ ์—†๋‹ค.(alert, confirm ๋“ฑ ๋ถˆ๊ฐ€๋Šฅ)

    firefox์ œ์™ธํ•˜๊ณ  ๋ธŒ๋ผ์šฐ์ € ํ˜ธํ™˜์„ฑ์ด ์™„๋ฒฝํ•˜์ง€ ์•Š๋‹ค.

    (ํฌ๋กฌ์—์„œ๋Š” ๋’ค๋กœ๊ฐ€๊ธฐ ์ˆ˜ํ–‰์‹œ js๋ฅผ ์žฌ์‹คํ–‰ํ•˜๊ณ  firefox๋Š” BFCache๋ฅผ ์‚ฌ์šฉํ•ด์„œ ์ €์žฅํ•ด ๋†“์€ ์ด์ „ ํŽ˜์ด์ง€๋ฅผ ๋ฐ”๋กœ ๋กœ๋“œํ•˜๋Š”๋ฐ ๋’ค๋กœ๊ฐ€๊ธฐ ์ˆ˜ํ–‰์‹œ์—๋„ ๋™์ž‘ํ•˜๋Š” ์ฝ”๋“œ๋ฅผ ๋ณดํ†ต pageshow, pagehide์— ๋„ฃ๋Š”๋‹ค๊ณ  ํ•œ๋‹ค.)

    • ์‚ฌ์šฉ๋ฐฉ๋ฒ•
    window.addEventListener('pagehide', function(event) {
      if (event.persisted) {
        "The page was cached by the browser"
      } else {
        "The page was NOT cached by the browser"
      }
    });
    

  1. unload

    pagehide์™€ ๋น„์Šทํ•˜๋‹ค.

    ์ทจ์†Œ๊ฐ€ ๋ถˆ๊ฐ€๋Šฅํ•˜๋‹ค.

    ์‚ฌ์šฉ์ž์—๊ฒŒ ๋ญ”๊ฐ€ ํ‘œ์‹œ๋ฅผ ํ•ด์ค„ ์ˆ˜ ์—†๋‹ค.(alert, confirm ๋“ฑ ๋ถˆ๊ฐ€๋Šฅ)

    ๋ชจ๋“  ๋ธŒ๋ผ์šฐ์ €์—์„œ ์ž˜ ํ˜ธํ™˜๋œ๋‹ค.

    • ์‚ฌ์šฉ๋ฐฉ๋ฒ•
    window.addEventListener('unload', function(event) {
    ...
    });
    

์œ„ ์ด๋ฒคํŠธ๋“ค์˜ ๋‹จ์ 

  • ์ฃผ์†Œ๊ฐ€ ๋ฐ”๋€Œ๋Š” ๊ฒฝ์šฐ์— ๋ฌด์กฐ๊ฑด ๋ฐœ์ƒํ•œ๋‹ค. ์–ด๋–ค ์ฃผ์†Œ๋กœ ์ด๋™๋  ๊ฒƒ์ธ์ง€ ์•Œ ์ˆ˜ ์žˆ์œผ๋ฉด url์„ ํŒŒ์‹ฑํ•ด์„œ ์–ด๋–ป๊ฒŒ๋“  ํ•  ์ˆ˜ ์žˆ์„์ง€ ๋ชจ๋ฅด์ง€๋งŒ ์ด๋™ ๋  ์ฃผ์†Œ๋ฅผ ์•Œ ์ˆ˜ ์žˆ๋Š” ๋ฐฉ๋ฒ•์€ ์—†๋Š” ๊ฒƒ ๊ฐ™๋‹ค.
  • ์ƒˆ๋กœ๊ณ ์นจ ํ•  ๋•Œ๋„ ๋ฐœ์ƒํ•œ๋‹ค.
  • ์‚ฌ์šฉ์ž์—๊ฒŒ ์›ํ•˜๋Š” ๋ฉ”์‹œ์ง€๋ฅผ ํ‘œ์‹œํ•ด ์ค„ ์ˆ˜ ์—†๋‹ค.

๋ฐฉ๋ฒ• 2. session์ด ์ข…๋ฃŒ๋˜๋ฉด ๋กœ๊ทธ์•„์›ƒ ์‹œ๊ฐ„์„ ๊ฐฑ์‹ ํ•˜์ž.

HttpSessionListener๋ฅผ ์‚ฌ์šฉํ•ด์„œ sessionDestroyed๋  ๋•Œ logoutAt ๊ฐฑ์‹ 

  • HttpSessionListener ์‚ฌ์šฉ๋ฒ•

    1. ๋ฆฌ์Šค๋„ˆ ํด๋ž˜์Šค์— @WebListener ๋ถ™์ธ๋‹ค.
    @WebListener
    public class CustomSessionListener implements HttpSessionListener {
    
        private static final Logger log = LoggerFactory.getLogger(CustomSessionListener.class);
    
        @Override
        public void sessionCreated(HttpSessionEvent se) {
            log.info("์„ธ์…˜์ด ์—ฐ๊ฒฐ๋˜์—ˆ์Šต๋‹ˆ๋‹ค. session ID : {}", se.getSession().getId());
        }
    
        @Override
        public void sessionDestroyed(HttpSessionEvent se) {
            log.info("์„ธ์…˜์ด ๋งŒ๋ฃŒ๋˜์—ˆ์Šต๋‹ˆ๋‹ค. session ID : {}", se.getSession().getId());
        }
    }
    
    1. Application์— @ServletComponentScan ์ ์šฉ
    @ServletComponentScan
    @SpringBootApplication
    public class TurkeyApplication {
    
    • Trigger

      request.getSession() โ†’ sessionCreated

      session.invalidate() โ†’ sessionDestroyed

    ๋ฌธ์ œ์  : ๋ธŒ๋ผ์šฐ์ €๊ฐ€ ์ข…๋ฃŒ๋˜๊ฑฐ๋‚˜ ๋‹ค๋ฅธ ์‚ฌ์ดํŠธ๋กœ ์˜ฎ๊ธด๋‹ค๊ณ  ํ•ด์„œ ์„ธ์…˜์ด ๋งŒ๋ฃŒ๋˜์ง€ ์•Š๋Š”๋‹ค.

๋ฐฉ๋ฒ• 3. timeout์„ ๋˜์—ˆ์„ ๋•Œ ๋กœ๊ทธ์•„์›ƒ ์‹œ๊ฐ„์„ ๊ฐฑ์‹ ํ•˜๋Š” ๋ฐฉ๋ฒ•์„ ์‚ฌ์šฉํ•˜์ž (ํ•ด๊ฒฐ ๋ฐฉ๋ฒ•)

2๋ฒˆ ๋ฐฉ๋ฒ•๊ณผ ์ฝ”๋“œ๋Š” ๋™์ผ

session timeout โ†’ sessionDestroyed ํ™œ์šฉ

  • timeout ์„ค์ • ๋ฐฉ๋ฒ•

    • global session timeout

    ๊ธฐ๋ณธ 1800์ดˆ

    60์ดˆ ๋‹จ์œ„ ๋‚ด๋ฆผ์œผ๋กœ ์„ค์ •๋œ๋‹ค.(179 โ†’ 120)

    60์ดˆ๋ฏธ๋งŒ ์„ค์ • ๋ถˆ๊ฐ€๋Šฅ

    30m โ†’ 30๋ถ„

        # application.yml -> global session timeout
        server:
          servlet:
            session:
              timeout: 60
    
    • current session timeout
        HttpSession session = request.getSession();
        session.setMaxInactiveInterval(60);
    

๋ฌธ์ œ์  2. ๋กœ๊ทธ์ธ ์ค‘์ธ๋ฐ ๋‹ค์‹œ ๋กœ๊ทธ์ธํ•˜๋Š” ๊ฒฝ์šฐ ์ด์ „ ๋กœ๊ทธ์ธ์ด timeout๋˜๋ฉด์„œ ๋‘๋ฒˆ์งธ ๋กœ๊ทธ์ธํ•œ ์œ ์ €๊ฐ€ ๋กœ๊ทธ์ธ ์ƒํƒœ์ธ๋ฐ๋„ ๋กœ๊ทธ์•„์›ƒ ๋  ์ˆ˜ ์žˆ๋‹ค.

๋ฌธ์ œ ์ƒํ™ฉ : login1 โ†’ ๋ธŒ๋ผ์šฐ์ € ๊ทธ๋ƒฅ ์ข…๋ฃŒ โ†’ login2 โ†’ login1 session timeout

ํ•ด๊ฒฐ๋ฐฉ๋ฒ•

login2์—์„œ ์œ ์ €๊ฐ€ login์ƒํƒœ์ด๋ฉด logoutAt์„ ๊ฐฑ์‹ ํ•˜๊ณ  loginํ•œ๋‹ค.

login1 session timeout์ผ๋•Œ logout ์ƒํƒœ์ด๋ฉด logoutAt์„ ๊ฐฑ์‹ ํ•˜์ง€ ์•Š๋Š”๋‹ค.(logoutํ• ๋•Œ logout ์ƒํƒœ์ด๋ฉด logoutAt์„ ๊ฐฑ์‹ ํ•˜์ง€ ์•Š๋Š”๋‹ค.)

login ์‹œ๊ฐ„์ด 30 ๋ถ„ ์•ˆ์ชฝ์ด๋ฉด session timeout์œผ๋กœ๋Š” logoutAt์„ ๊ฐฑ์‹ ํ•˜์ง€ ์•Š๋Š”๋‹ค.

// LoginService
public UserSession login(final LoginRequest loginRequest) {
    try {
        User user = userService.findByEmail(loginRequest.getEmail());
        user.matchPassword(loginRequest.getPassword());
        checkLoginAndUpdateLogoutAt(user);
        user.updateLoginAt(LocalDateTime.now());
        return UserSession.from(user);
    } catch (Exception e) {
        throw new LoginFailException(e.getMessage());
    }
}

public void logout(final Long userId) {
    User user = userService.findById(userId);
    checkLoginAndUpdateLogoutAt(user);
}

private void checkLoginAndUpdateLogoutAt(User user) {
    if (user.isLogin()) {
        user.updateLogoutAt(LocalDateTime.now());
    }
}

๋ฌธ์ œ์  3. ์ƒˆ๋กœ๊ณ ์นจ ์•ˆํ•ด๋„ ์นœ๊ตฌ๊ฐ€ ์ ‘์† ์ข…๋ฃŒํ•˜๋ฉด ๋กœ๊ทธ ์•„์›ƒ์œผ๋กœ ํ‘œ์‹œ ํ•˜๋ ค๋ฉด?

websocket์„ ํ™œ์šฉํ•ด์„œ ๋กœ๊ทธ์ธํ•˜๊ฑฐ๋‚˜ ๋กœ๊ทธ์•„์›ƒ ํ•  ๋•Œ ์นœ๊ตฌ๋“ค์—๊ฒŒ ๋ฉ”์„ธ์ง€๋ฅผ ๋ณด๋‚ด์ฃผ๋ฉด ๋˜์ง€์•Š์„๊นŒ?