Skip to content
Caution: You are browsing the legacy symfony 1.x part of this website.

symfony advent calendar day six: security and form validation

Language

์ง€๋‚œ ์ค„๊ฑฐ๋ฆฌ

5์ผ์งธ ์—๋Š” ํ…œํ”Œ๋ฆฟ๊ณผ ์•ก์…˜๋“ค์„ ๊ฑด๋“œ๋ ค๋ณด์•˜์Šต๋‹ˆ๋‹ค. ํผ๊ณผ ํŽ˜์ด์ €์— ๊ด€ํ•ด์„œ๋Š” ๋น ์‚ญํ•˜๊ฒŒ ์•Œ๊ฒŒ ๋˜์…จ์„ ๊ฒƒ์ž…๋‹ˆ๋‹ค. ์˜ค๋Š˜ ํ•˜๊ฒŒ ๋  ๋ถ€๋ถ„์€ ๋กœ๊ทธ์ธ๋˜์ง€ ์•Š์•˜๊ฑฐ๋‚˜ ์Šน์ธ๋˜์ง€ ์•Š์€ ์‚ฌ์šฉ์ž๊ฐ€ ํŠน์ • ํŽ˜์ด์ง€๋ฅผ ์ ‘๊ทผํ•˜๋Š” ๊ฒƒ์„ ๋ง‰๋„๋ก ํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค. ์ด ๊ณผ์ •์—์„œ ํผ ๊ฐ’์„ ํ™•์ธํ•˜๋Š” ๊ฒƒ๋„ ํ•จ๊ป˜ ์‚ดํŽด๋ณผ ๊ณ„ํš์ž…๋‹ˆ๋‹ค. ์ด ๊ณผ์ •์—์„œ ์‚ฌ์šฉ์ž ํด๋ž˜์Šค๊ฐ€ ์‚ฌ์šฉ๋  ๊ฒƒ์ด๋‹ˆ, ์ด์— ๋Œ€ํ•ด ์•„์ง ์ž˜ ๋ชจ๋ฅด์‹ ๋‹ค๋ฉด ์˜จ๋ผ์ธ ๋ฌธ์„œ์ค‘ ์‚ฌ์šฉ์ž ํ™•์žฅ ๋ถ€๋ถ„ ์„ ์‚ดํŽด๋ณด์‹œ๊ธฐ ๋ฐ”๋ž๋‹ˆ๋‹ค.

๋กœ๊ทธ์ธ ํผ๊ฐ’ ํ™•์ธํ•˜๊ธฐ (Validation)

๊ฒ€์ฆ (Validation) ํŒŒ์ผ

๋กœ๊ทธ์ธ ํผ์—๋Š” nickname ๊ณผ password ๋ฅผ ์ž…๋ ฅํ•  ์ˆ˜ ์žˆ๋„๋ก ๋˜์–ด ์žˆ์Šต๋‹ˆ๋‹ค. ํ•˜์ง€๋งŒ ๋งŒ์•ฝ ์‚ฌ์šฉ์ž๊ฐ€ ๋ถ€์ •ํ™•ํ•œ ์ •๋ณด๋ฅผ ์ž…๋ ฅํ•œ๋‹ค๋ฉด ์–ด๋–ป๊ฒŒ ๋ ๊นŒ์š”? ์ด๋Ÿฌํ•œ ๊ฒฝ์šฐ์— ๋Œ€์ฒ˜ํ•˜๊ธฐ ์œ„ํ•ด์„œ ์‹ฌํฌ๋‹ˆ๋Š” ํผ๊ฐ’ ํ™•์ธ ์‹œ์Šคํ…œ์ด ์žˆ์Šต๋‹ˆ๋‹ค. /fronted/modules/user/validate ๋””๋ ‰ํ† ๋ฆฌ์— login.yml ํŒŒ์ผ์„ ์ƒ์„ฑํ•˜์—ฌ ์•„๋ž˜ ๋‚ด์šฉ์„ ์ž…๋ ฅํ•ฉ๋‹ˆ๋‹ค.

methods:
  post: [nickname, password]

names:
  nickname:
    required:     true
    required_msg: your nickname is required
    validators:   nicknameValidator

  password:
    required:     true
    required_msg: your password is required

nicknameValidator:
    class:        sfStringValidator
    param:
      min:        5
      min_error:  nickname must be 5 or more characters

๋จผ์ € methods ์•„๋ž˜๋กœ ๊ฒ€ํ† ํ•ด์•ผํ•  ํ•„๋“œ๋“ค์„ ํผ์ด ์ „์†ก๋œ ๋ฉ”์˜๋“œ์™€ ํ•จ๊ป˜ ์ ์Šต๋‹ˆ๋‹ค (POST ๋ฉ”์˜๋“œ๋งŒ ์‚ฌ์šฉํ•˜๋Š” ์ด์œ ๋Š” GET ๋ฉ”์˜๋“œ๋กœ ๋„˜์–ด์˜จ ๊ฐ’๋“ค์€ ๋กœ๊ทธ์ธ ํผ์„ ํ‘œ์‹œํ•˜๊ธฐ ์œ„ํ•ด ์ „๋‹ฌ๋œ ๊ฐ’๋“ค์ด๊ธฐ ๋•Œ๋ฌธ์— ๊ฒ€ํ† ํ•  ํ•„์š”๊ฐ€ ์—†๊ธฐ ๋•Œ๋ฌธ์ž…๋‹ˆ๋‹ค). ๊ทธ๋ฆฌ๊ณ  names ์•„๋ž˜๋กœ ๊ฐ๊ฐ์˜ ํ•„๋“œ๊ฐ€ ๊ฐ€์ ธ์•ผ ํ•˜๋Š” ๊ฐ’๋“ค์„ ์—๋Ÿฌ๋ฉ”์‹œ์ง€์™€ ํ•จ๊ป˜ ์ž…๋ ฅํ•ฉ๋‹ˆ๋‹ค. ๋งˆ์ง€๋ง‰์œผ๋กœ๋Š” 'nickname' ํ•„๋“œ๊ฐ€ ๊ฐ€์ ธ์•ผ ํ•˜๋Š” ๊ฐ’์— ๋Œ€ํ•œ ๊ทœ์น™์„ ์ •ํ•ด์ง„ ํ•„๋“œ๋ฅผ ์‚ฌ์šฉํ•ด ์ •์˜ํ•ฉ๋‹ˆ๋‹ค. ์ด ์˜ˆ์ œ์—์„œ๋Š” ์‹ฌํฌ๋‹ˆ๊ฐ€ ๊ธฐ๋ณธ์ ์œผ๋กœ ์ œ๊ณตํ•˜๋Š” sfStringValidator ๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ๋ฌธ์ž์—ด์˜ ํ˜•ํƒœ๋ฅผ ๊ฒ€์‚ฌํ•ฉ๋‹ˆ๋‹ค (์‹ฌํฌ๋‹ˆ์˜ ํผ ๊ธฐ๋ณธ ๊ฒ€ํ†  ๊ทœ์น™๋“ค์„ ์•Œ๊ณ  ์‹ถ์œผ์‹œ๋‹ค๋ฉด ์˜จ๋ผ์ธ ๋ฌธ์„œ์ค‘ ํผ๊ฐ’ ํ™•์ธํ•˜๊ธฐ ๋ถ€๋ถ„ ์„ ์ฐธ๊ณ ํ•˜์‹œ๊ธฐ ๋ฐ”๋ž๋‹ˆ๋‹ค.

์—๋Ÿฌ ์ฒ˜๋ฆฌ

์ž ๊ทธ๋Ÿผ ์‚ฌ์šฉ์ž๊ฐ€ ์ž˜๋ชป๋œ ๋ฐ์ดํ„ฐ๋ฅผ ์ž…๋ ฅํ•  ๊ฒฝ์šฐ ์–ด๋–ป๊ฒŒ ๋˜๋‚˜์š”? login.yml ํŒŒ์ผ์— ์“ฐ์—ฌ์ง„ ์กฐ๊ฑด์ด ๋งŒ์กฑ๋˜์ง€ ์•Š์„ ๊ฒฝ์šฐ์— ์ปจํŠธ๋กค๋Ÿฌ๋Š” ์›๋ž˜ ํ˜ธ์ถœํ•˜๋„๋ก๋œ userAction ํด๋ž˜์Šค์˜ executeLogin() ๋ฉ”์˜๋“œ ๋Œ€์‹ ์— userAction ํด๋ž˜์Šค์˜ handleErrorLogin() ๋ฉ”์˜๋“œ๋ฅผ ํ˜ธ์ถœํ•ฉ๋‹ˆ๋‹ค. ๋งŒ์•ฝ ํ•ด๋‹น ๋ฉ”์˜๋“œ๊ฐ€ ์กด์žฌํ•˜์ง€ ์•Š๋Š”๋‹ค๋ฉด ๊ธฐ๋ณธ์ ์œผ๋กœ loginError.php ๋ฅผ ์ถœ๋ ฅํ•ฉ๋‹ˆ๋‹ค. ์‚ฌ์‹ค ๊ทธ๊ฒŒ ๊ธฐ๋ณธ handleError() ๋ฉ”์˜๋“œ๊ฐ€ ํ•˜๋Š”์ผ์ž…๋‹ˆ๋‹ค.

public function handleError()
{
  return sfView::ERROR;
}

๊ทธ๋Ÿผ ์ƒˆ๋กœ์šด ํ…œํ”Œ๋ฆฟ์„ ์ž‘์„ฑํ•ด์•ผ ํ•˜๋Š” ๊ฑด๊ฐ€์š”? ์—๋Ÿฌ ํŽ˜์ด์ง€๋ฅผ ์ž‘์„ฑํ•˜๋Š” ๋Œ€์‹ ์—, ์—๋Ÿฌ ๋ฉ”์‹œ์ง€๋ฅผ ๋ฌธ์ œ๊ฐ€ ์ƒ๊ธด ํผ ์˜†์— ์ถœ๋ ฅํ•˜๊ณ  ๋กœ๊ทธ์ธ ํผ์„ ๋‹ค์‹œ ๋ณด์—ฌ์ฃผ๋„๋ก ํ•  ๊ฒƒ์ž…๋‹ˆ๋‹ค. ๊ทธ๋Ÿผ ์—๋Ÿฌ๊ฐ€ ๋ฐœ์ƒํ–ˆ์„๋•Œ์˜ ๋Œ€์ฒ˜๋ฐฉ์‹์„ ์ˆ˜์ •ํ•˜๊ณ  ๋กœ๊ทธ์ธ ํ…œํ”Œ๋ฆฟ์ธ loginSuccess.php ํ…œํ”Œ๋ฆฟ์„ ์ˆ˜์ •ํ•˜๋„๋ก ํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค.

public function handleErrorLogin()
{
  return sfView::SUCCESS;
}

์ฐธ๊ณ : ์•ก์…˜์ด๋ฆ„๊ณผ ๊ทธ๊ฒƒ์˜ return ๊ฐ’, ๊ทธ๋ฆฌ๊ณ  ํ…œํ”Œ๋ฆฟ ํŒŒ์ผ๊ณผ ๊ด€๊ณ„๋œ ์ด๋ฆ„ ๊ทœ์•ฝ์€ ์˜จ๋ผ์ธ ๋ฌธ์„œ์ค‘ ๋ทฐ ๋ถ€๋ถ„ ์—์„œ ๋‹ค๋ฃจ๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค.

ํ…œํ”Œ๋ฆฟ ์—๋Ÿฌ ํ—ฌํผ๋“ค

์ด์ œ loginSuccess.php ๊ฐ€ ๋‹ค์‹œ ํ˜ธ์ถœ๋˜๋„๋ก ํ•˜์˜€์œผ๋‹ˆ ํ…œํ”Œ๋ฆฟ์— ์—๋Ÿฌ๋ฅผ ์ถœ๋ ฅํ•˜๋„๋ก ํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค. Validation ํ—ฌํผ ๋ชจ์Œ์˜ form_error() ํ—ฌํผ๋ฅผ ์‚ฌ์šฉํ•  ๊ฒƒ์ž…๋‹ˆ๋‹ค. ๋‘๊ฐœ์˜ form-row ๋ถ€๋ถ„์„ ์•„๋ž˜์™€ ๊ฐ™์ด ์ˆ˜์ •ํ•ฉ๋‹ˆ๋‹ค.

<?php use_helper('Validation') ?>
 
<div class="form-row">
  <?php echo form_error('nickname') ?>
  <label for="nickname">nickname:</label>
  <?php echo input_tag('nickname', $sf_params->get('nickname')) ?>
</div>
 
<div class="form-row">
  <?php echo form_error('password') ?>
  <label for="password">password:</label>
  <?php echo input_password_tag('password') ?>
</div>

form_error() ํ—ฌํผ๋Š” ์ธ์ž๋กœ ๋ฐ›์€ ๊ฐ’์— ํ•ด๋‹นํ•˜๋Š” ํ•„๋“œ์— ์—๋Ÿฌ๊ฐ€ ๋ฐœ์ƒ๋˜์—ˆ์„๋•Œ, login.yml ์— ์ •์˜๋œ ์—๋Ÿฌ ๋ฉ”์‹œ์ง€๋ฅผ ์ถœ๋ ฅํ•  ๊ฒƒ์ž…๋‹ˆ๋‹ค.

์‚ฌ์šฉ์ž ์ด๋ฆ„์— 5๊ธ€์ž๋ณด๋‹ค ์งง๊ฒŒ ์ž…๋ ฅํ•˜๊ฑฐ๋‚˜ ๋‘˜ ์ค‘์˜ ์•„๋ฌด๊ฒƒ์ด๋‚˜ ๋นˆ ์นธ์œผ๋กœ ๋‚จ๊ฒจ๋‘”์ฑ„ ํ™•์ธ๋ฒ„ํŠผ์„ ๋ˆŒ๋Ÿฌ์„œ ํผ๊ฐ’ ํ™•์ธ์ด ์ž˜ ์ž‘๋™ํ•˜๋Š”์ง€ ์‚ดํŽด๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค. ์—๋Ÿฌ ๋ฉ”์„ธ์ง€๊ฐ€ ๋งˆ์ˆ ์ฒ˜๋Ÿผ ๊ด€๊ณ„๋œ ํ•„๋“œ์— ๋‚˜ํƒ€๋‚˜๋Š” ๊ฒƒ์„ ํ™•์ธํ•˜์‹ค ์ˆ˜ ์žˆ์œผ์‹ค ๊ฒƒ์ž…๋‹ˆ๋‹ค.

๋กœ๊ทธ์ธ ํผ ์—๋Ÿฌ๋ฉ”์‹œ์ง€

์ด์ œ ๋น„๋ฐ€๋ฒˆํ˜ธ๊ฐ€ ํ•„์ˆ˜ํ•ญ๋ชฉ์ด ๋˜์—ˆ์Šต๋‹ˆ๋‹ค๋งŒ, ์•„์ง ์•„๋ฌด๋Ÿฐ ๋น„๋ฐ€๋ฒˆํ˜ธ๋„ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์— ์ €์žฅ๋˜์–ด ์žˆ์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ์‚ฌ์‹ค ์•„๋ฌด ๋น„๋ฐ€๋ฒˆํ˜ธ๋‚˜ ์ž…๋ ฅํ•˜๋”๋ผ๋„ ๋กœ๊ทธ์ธ์ด ๋  ๊ฒƒ์ž…๋‹ˆ๋‹ค. ์ƒ๋‹นํžˆ ์ข‹์€ ๋ณด์•ˆ์ฒด๊ณ„์ด์ฃ ?

์—๋Ÿฌ ๋ฉ”์„ธ์ง€ ์Šคํƒ€์ผ ์ •์˜

์œ„์—์„œ ํผ์„ ํ…Œ์ŠคํŠธํ•˜์‹ค๋•Œ ์ €ํฌ๊ฐ€ ์บก์ถฐํ•œ ์ด๋ฏธ์ง€์™€๋Š” ๋‹ค๋ฅด๊ฒŒ ๋‚˜ํƒ€๋‚˜๋Š”๊ฒƒ์„ ๋ณด์…จ์„ ๊ฒƒ์ž…๋‹ˆ๋‹ค. ๊ทธ๊ฒƒ์€ ์šฐ๋ฆฌ๊ฐ€ web/main.css ์— ์žˆ๋Š” .form_error ์Šคํƒ€์ผ ํด๋ž˜์Šค๋ฅผ ์ •์˜ํ•˜์˜€๊ธฐ ๋•Œ๋ฌธ์ž…๋‹ˆ๋‹ค. form_error() ํ—ฌํผ๊ฐ€ ์—๋Ÿฌ๋ฉ”์‹œ์ง€๋ฅผ ์ถœ๋ ฅํ• ๋•Œ๋Š” ๊ธฐ๋ณธ์ ์œผ๋กœ .form_error ์Šคํƒ€์ผ์ด ์‚ฌ์šฉ๋ฉ๋‹ˆ๋‹ค. ์•„๋ž˜์™€ ๊ฐ™์ด ํ•ด๋‹น ์Šคํƒ€์ผ์„ ์ •์˜ํ•˜๋ฉด ์บก์ถฐํ•œ ์ด๋ฏธ์ง€์™€ ๋™์ผํ•œ ์Šคํƒ€์ผ์„ ํ™•์ธํ•˜์‹ค ์ˆ˜ ์žˆ์œผ์‹ค ๊ฒƒ์ž…๋‹ˆ๋‹ค.

.form_error
{
  padding-left: 85px;
  color: #d8732f;
}

์‚ฌ์šฉ์ž ์ธ์ฆํ•˜๊ธฐ

์‚ฌ์šฉ์ž ํผ๊ฐ’ ํ™•์ธ ๊ทœ์น™

์–ด์ œ ์šฐ๋ฆฌ๋Š” login ์•ก์…˜์—์„œ ์‚ฌ์šฉ์ž์ด๋ฆ„์ด ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์— ์กด์žฌํ•˜๋Š”์ง€๋ฅผ ํ™•์ธํ•˜๋„๋ก ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ–ˆ์Šต๋‹ˆ๋‹ค. ์ด ์ž‘์—…๋„ ํผ๊ฐ’ ํ™•์ธ ๋ถ€๋ถ„์—์„œ ํ•˜๋Š”๊ฒƒ์ด ๋งž์„ ๊ฒƒ ๊ฐ™์Šต๋‹ˆ๋‹ค. ์ด์ œ ๊ทธ ์ฝ”๋“œ๋ฅผ ์•ก์…˜ํŒŒ์ผ์—์„œ ๋“ค์–ด๋‚ด๊ณ  ์‚ฌ์šฉ์ž ํผ๊ฐ’ ํ™•์ธ ๊ทœ์น™์„ ์ƒ์„ฑํ•˜์—ฌ ๊ทธ์ชฝ์œผ๋กœ ๋ณด๋‚ด๋„๋ก ํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค. ๋ณต์žกํ•ด ๋ณด์ด์‹ ๋‹ค๊ณ ์š”? ์‚ฌ์‹ค์€ ๊ทธ๋ ‡์ง€๋งŒ๋„ ์•Š์Šต๋‹ˆ๋‹ค. login.yml ํŒŒ์ผ์„ ์—ด๊ณ  ์•„๋ž˜์™€ ๊ฐ™์ด ์ˆ˜์ •ํ•ฉ๋‹ˆ๋‹ค.

...
names:
  nickname:
    required:      true
    required_msg:  your nickname is required
    validators:    [nicknameValidator, userValidator]
...
userValidator:
    class:         myLoginValidator
    param:
      password:    password
      login_error: this account does not exist or you entered a wrong password

nickname ํ•„๋“œ์— myLoginValidator ๋ผ๋Š” ์ƒˆ๋กœ์šด ํผ๊ฐ’ ํ™•์ธ ๊ทœ์น™์„ ์ถ”๊ฐ€ํ•˜์˜€์Šต๋‹ˆ๋‹ค. ์ด ๊ทœ์น™์ด ์•„์ง์€ ์กด์žฌํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค๋งŒ, ์‚ฌ์šฉ์ž๋ฅผ ์ธ์ฆํ•˜๊ธฐ ์œ„ํ•ด์„œ๋Š” ๋น„๋ฐ€๋ฒˆํ˜ธ๊ฐ€ ํ•„์š”ํ•˜๊ธฐ๋•Œ๋ฌธ์—, password ๋ผ๋Š” ์ด๋ฆ„์œผ๋กœ ๋น„๋ฐ€๋ฒˆํ˜ธ๋ฅผ ์ „๋‹ฌํ•ฉ๋‹ˆ๋‹ค.

๋น„๋ฐ€๋ฒˆํ˜ธ ์ €์žฅ

์ž ์‹œ๋งŒ์š”. ์šฐ๋ฆฌ ๋ฐ์ดํ„ฐ ๋ชจ๋ธ๊ณผ ํ…Œ์ŠคํŠธ ๋ฐ์ดํ„ฐ์—๋Š” ๋น„๋ฐ€๋ฒˆํ˜ธ๊ฐ€ ์•„์ง ์—†์Šต๋‹ˆ๋‹ค. ๋จผ์ € ์‚ฌ์šฉ์ž๋“ค์˜ ๋น„๋ฐ€๋ฒˆํ˜ธ๋ฅผ ์ €์žฅํ•˜๋„๋ก ํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค. ํ•˜์ง€๋งŒ ์•„์‹œ๋‹ค์‹œํ”ผ ๋น„๋ฐ€๋ฒˆํ˜ธ๋ฅผ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์— ์ผ๋ฐ˜ ํ‰๋ฌธ์œผ๋กœ ์ €์žฅํ•˜๋Š” ๊ฒƒ์€ ๋ณด์•ˆ์ƒ ์ข‹์ง€ ์•Š๊ธฐ ๋•Œ๋ฌธ์—, ์šฐ๋ฆฌ๋Š” ๋น„๋ฐ€๋ฒˆํ˜ธ๋ฅผ ๋žœ๋ค ํ‚ค๋กœ sha1 ํ•ด์‰ฌ ํ•˜์—ฌ ์ €์žฅํ•˜๋„๋ก ํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค. ๋งŒ์•ฝ ์ด '์†Œ๊ธˆ' (์ฃผ - ๋น„๋ฐ€๋ฒˆํ˜ธ์— ๋”ํ•˜๋Š” ๋žœ๋คํ‚ค๋ฅผ 'salt' ๋ผ๊ณ  ํ•ฉ๋‹ˆ๋‹ค) ๋ฐฉ์‹์ด ์ต์ˆ™ํ•˜์ง€ ์•Š๋‹ค๋ฉด ๋น„๋ฐ€๋ฒˆํ˜ธ ๊นจํŠธ๋ฆฌ๊ธฐ ์‹ค๋ก€ ๋ฅผ ํ•œ ๋ฒˆ ๋ณด์‹œ๊ธฐ ๋ฐ”๋ž๋‹ˆ๋‹ค.

schema.xml ํŒŒ์ผ์„ ์—ฌ์‹œ๊ณ  User ํ…Œ์ด๋ธ”์— ๋‹ค์Œ์„ ์ถ”๊ฐ€ํ•ฉ๋‹ˆ๋‹ค.

<column name="email" type="varchar" size="100" />
<column name="sha1_password" type="varchar" size="40" />
<column name="salt" type="varchar" size="32" />

symfony propel-build-model ์„ ์‹คํ–‰ํ•˜์…”์„œ ๋ชจ๋ธ์„ ์ƒˆ๋กœ ์ž‘์„ฑํ•ฉ๋‹ˆ๋‹ค. symfony propel-build-sql ๋ช…๋ น์„ ํ†ตํ•ด์„œ ์ƒ์„ฑํ•œ lib.model.schema.sql ์„ ์‚ฌ์šฉํ•˜๋˜์ง€ ์ง์ ‘ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์— ์ ‘์†ํ•˜์…”์„œ ํ•˜์‹œ๋˜์ง€, ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์— ์ด ํ•„๋“œ๋“ค์„ ์ถ”๊ฐ€ํ•˜์—ฌ์•ผ ํ•ฉ๋‹ˆ๋‹ค. ์ด์ œ askeet/lib/model/User.php ํŒŒ์ผ์„ ์—ด๊ณ  setPassword() ๋ฉ”์˜๋“œ๋ฅผ ์ถ”๊ฐ€ํ•ฉ๋‹ˆ๋‹ค.

public function setPassword($password)
{
  $salt = md5(rand(100000, 999999).$this->getNickname().$this->getEmail());
  $this->setSalt($salt);
  $this->setSha1Password(sha1($salt.$password));
}

์ผ๋ฐ˜์ ์ธ ๋น„๋ฐ€๋ฒˆํ˜ธ ์ €์žฅ๋ฐฉ์‹๊ณผ ๋‹ค๋ฅผ๋ฐ”์—†์–ด๋ณด์ด์ง€๋งŒ, ์œ„ ๋ฐฉ์‹์œผ๋กœ ์ž„์˜์˜ ์†Œ๊ธˆ (32๊ธ€์ž ํ•ด์‰ฌ๋œ ์ž„์˜์˜ ๋ฌธ์ž์—ด) ๊ณผ ํ•ด์‰ฌ๋œ ๋น„๋ฐ€๋ฒˆํ˜ธ (40 ๊ธ€์ž ๋ฌธ์ž์—ด) ๋ฅผ ์ €์žฅํ•ฉ๋‹ˆ๋‹ค.

ํ…Œ์ŠคํŠธ ๋ฐ์ดํ„ฐ์— ๋น„๋ฐ€๋ฒˆํ˜ธ ์ถ”๊ฐ€

3์ผ์งธ ์— ๋งŒ๋“ค์—ˆ๋˜ ๋ฐ์ดํ„ฐ ํŒŒ์ผ ๊ธฐ์–ต๋‚˜์‹œ๋‚˜์š”? ๋น„๋ฐ€๋ฒˆํ˜ธ์™€ ์ด๋ฉ”์ผ์„ ํ…Œ์ŠคํŠธ ๋ฐ์ดํ„ฐ์— ์ถ”๊ฐ€ํ•ฉ๋‹ˆ๋‹ค. askeet/data/fixtures/test_data.yml ํŒŒ์ผ์„ ์—ด๊ณ  ์•„๋ž˜์™€ ๊ฐ™์ด ์ˆ˜์ •ํ•ฉ๋‹ˆ๋‹ค.

User:
  ...
  fabien:
    nickname:   fabpot
    first_name: Fabien
    last_name:  Potencier
    password:   symfony
    email:      fp@example.com

  francois:
    nickname:   francoisz
    first_name: Franรงois
    last_name:  Zaninotto
    password:   adventcal
    email:      fz@example.com

User ํด๋ž˜์Šค์— setPassword() ๋ฉ”์˜๋“œ๊ฐ€ ์ด๋ฏธ ์ •์˜๋˜์–ด ์žˆ์œผ๋ฏ€๋กœ, sfPropelData ๊ฐ์ฒด๋Š” sha1_password ์™€ salt ์ปฌ๋Ÿผ์„ ์ž๋™์œผ๋กœ ์ƒ์„ฑํ•˜์—ฌ ์ค„ ๊ฒƒ์ž…๋‹ˆ๋‹ค. ์ด์ œ ์•„๋ž˜ ๋ช…๋ น์„ ํ˜ธ์ถœํ•ฉ๋‹ˆ๋‹ค.

$ php batch/load_data.php

์ฐธ๊ณ : sfPropelData ๊ฐ์ฒด๋Š” ์‹ค์ œ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ์ปฌ๋Ÿผ์— ๊ธฐ๋ฐ˜ํ•˜์—ฌ ์ƒ์„ฑ๋œ ๋ฉ”์˜๋“œ๊ฐ€ ์•„๋‹ˆ๋”๋ผ๋„ ์‚ฌ์šฉ์ด ๊ฐ€๋Šฅํ•ฉ๋‹ˆ๋‹ค.

์ด์— ๋Œ€ํ•œ ์ข€ ๋” ์ž์„ธํ•œ ๋‚ด์šฉ์€ ์˜จ๋ผ์ธ ๋ฌธ์„œ์ค‘ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ์ž๋ฃŒ ์ž…๋ ฅํ•˜๊ธฐ ๋ถ€๋ถ„ ์„ ์ฐธ์กฐํ•˜์‹œ๊ธฐ ๋ฐ”๋ž๋‹ˆ๋‹ค.

์ฐธ๊ณ : 'Anonymous Coward' ์‚ฌ์šฉ์ž์— ๋Œ€ํ•ด์„œ ๋น„๋ฐ€๋ฒˆํ˜ธ๋ฅผ ์ง€์ •ํ•  ํ•„์š”๋Š” ์—†์Šต๋‹ˆ๋‹ค. ์šฐ๋ฆฌ๋Š” ํ•ด๋‹น ์‚ฌ์šฉ์ž๋ฅผ ๋กœ๊ทธ์ธํ•˜์ง€ ๋ชป ํ•˜๋„๋ก ํ•  ๊ณ„ํš์ž…๋‹ˆ๋‹ค. ๊ทธ๋ฆฌ๊ณ  ์œ„ ๋น„๋ฐ€๋ฒˆํ˜ธ๋Š” ์šฐ๋ฆฌ๊ฐ€ ์‚ฌ์šฉํ•˜๋Š” ๋น„๋ฐ€๋ฒˆํ˜ธ๋“ค๊ณผ๋Š” ์ „ํ˜€ ์—ฐ๊ด€์ด ์—†์œผ๋‹ˆ ํ—›๊ณ ์ƒํ•˜์ง€ ๋งˆ์‹œ๊ธฐ ๋ฐ”๋ž๋‹ˆ๋‹ค.

์‚ฌ์šฉ์ž ํผ๊ฐ’ ํ™•์ธ ๊ทœ์น™

์ด์ œ myLoginValidator ๋ฅผ ์ž‘์„ฑํ•ด๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค. ํŒŒ์ผ์€ ๋ชจ๋“ˆ์ด ์ ‘๊ทผ ๊ฐ€๋Šฅํ•œ askeet/lib/ ๋‚˜ askeet/aps/frontend/lib/, ๋˜๋Š” askeet/apps/frontend/modules/user/lib/ ์ค‘ ์–ด๋А๊ณณ์— ๋งŒ๋“œ์…”๋„ ๋ฌด๋ฐฉํ•ฉ๋‹ˆ๋‹ค. ์ง€๊ธˆ์€ ์ „์ฒด ์–ดํ”Œ๋ฆฌ์ผ€์ด์…˜์—์„œ ์ด ๊ทœ์น™์„ ์‚ฌ์šฉํ•  ๊ฒƒ์ด๋ผ๊ณ  ๊ฐ€์ •ํ•˜๊ณ  askeet/apps/frontend/lib/ ์•„๋ž˜์— myLoginValidator.class.php ๋ผ๋Š” ์ด๋ฆ„์œผ๋กœ ํŒŒ์ผ์„ ๋งŒ๋“ค๋„๋ก ํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค.

<?php
 
class myLoginValidator extends sfValidator
{    
  public function initialize($context, $parameters = null)
  {
    // initialize parent
    parent::initialize($context);
 
    // set defaults
    $this->setParameter('login_error', 'Invalid input');
 
    $this->getParameterHolder()->add($parameters);
 
    return true;
  }
 
  public function execute(&$value, &$error)
  {
    $password_param = $this->getParameter('password');
    $password = $this->getContext()->getRequest()->getParameter($password_param);
 
    $login = $value;
 
    // anonymous is not a real user
    if ($login == 'anonymous')
    {
      $error = $this->getParameter('login_error');
      return false;
    }
 
    $c = new Criteria();
    $c->add(UserPeer::NICKNAME, $login);
    $user = UserPeer::doSelectOne($c);
 
    // nickname exists?
    if ($user)
    {
      // password is OK?
      if (sha1($user->getSalt().$password) == $user->getSha1Password())
      {
        $this->getContext()->getUser()->setAuthenticated(true);
        $this->getContext()->getUser()->addCredential('subscriber');
 
        $this->getContext()->getUser()->setAttribute('subscriber_id', $user->getId(), 'subscriber');
        $this->getContext()->getUser()->setAttribute('nickname', $user->getNickname(), 'subscriber');
 
        return true;
      }
    }
 
    $error = $this->getParameter('login_error');
    return false;
  }
}

ํผ๊ฐ’ ํ™•์ธ ํด๋ž˜์Šค (validator) ๊ฐ€ ์‹œ์ ์—์„œ - ์—ฌ๊ธฐ์„œ๋Š” login ํผ์ด ์ œ์ถœ๋œ ํ›„๊ฒ ์ง€์š” - initialize() ๋ฉ”์˜๋“œ๊ฐ€ ๋จผ์ € ํ˜ธ์ถœ๋ฉ๋‹ˆ๋‹ค. ํ•ด๋‹น ๋ฉ”์˜๋“œ๋Š” login_error ๋ฉ”์„ธ์ง€์˜ ๊ธฐ๋ณธ๊ฐ’์œผ๋กœ Invalid Input ์„ ์ €์žฅํ•˜๊ณ  login.yml ํŒŒ์ผ์˜ parma: ํ—ค๋” ์•„๋ž˜ ์žˆ๋˜ ๊ฐ’๋“ค์„ ํŒŒ๋ผ๋ฏธํ„ฐ ํ™€๋” ๊ฐ์ฒด๋กœ ๋ณ‘ํ•ฉํ•ฉ๋‹ˆ๋‹ค.

๊ทธ๋ฆฌ๊ณ ๋Š” execute() ๋ฉ”์˜๋“œ๊ฐ€ ํ˜ธ์ถœ๋ฉ๋‹ˆ๋‹ค. $password_param ์€ login.yml ํŒŒ์ผ์˜ password ํ—ค๋” ์•„๋ž˜์— ์žˆ๋˜ ํ•„๋“œ์˜ ์ด๋ฆ„์ž…๋‹ˆ๋‹ค. ์ด ํ•„๋“œ ์ด๋ฆ„์„ ์‚ฌ์šฉํ•˜์—ฌ ์‚ฌ์šฉ์ž ์š”์ฒญ๊ฐ’๋“ค ์ค‘์—์„œ ๋น„๋ฐ€๋ฒˆํ˜ธ๋ฅผ ์ฐพ์Šต๋‹ˆ๋‹ค. ๋”ฐ๋ผ์„œ $password ๋ณ€์ˆ˜์—๋Š” ์‹ค์ œ ์‚ฌ์šฉ์ž๊ฐ€ ์ž…๋ ฅํ•œ ๋น„๋ฐ€๋ฒˆํ˜ธ๊ฐ€ ๋“ค์–ด๊ฐ€๊ฒŒ ๋ฉ๋‹ˆ๋‹ค. $value ๋ณ€์ˆ˜์—๋Š” ํ˜„์žฌ ํ•„๋“œ์˜ ๊ฐ’, ์ฆ‰ myLoginValidator ํด๋ž˜์Šค๋Š” nickname ํ•„๋“œ๋ฅผ ํ™•์ธํ•˜๊ธฐ ์œ„ํ•ด ํ˜ธ์ถœ๋˜์—ˆ์œผ๋ฏ€๋กœ, ์‚ฌ์šฉ์ž๊ฐ€ ์ž…๋ ฅํ•œ ์ด๋ฆ„์ด ๋“ค์–ด๊ฐ€๊ฒŒ ๋ฉ๋‹ˆ๋‹ค. ๋“œ๋””์–ด ํผ๊ฐ’ ํ™•์ธ ํด๋ž˜์Šค๊ฐ€ ์‚ฌ์šฉ์ž๋ฅผ ํ™•์ธํ•˜๋Š”๋ฐ ํ•„์š”๋กœ ํ•˜๋Š” ๋ฐ์ดํ„ฐ๋“ค์ด ๋ชจ๋‘ ๋ชจ์˜€์Šต๋‹ˆ๋‹ค.

์ดํ›„์˜ ์ฝ”๋“œ๋“ค์€ login ์•ก์…˜์—์„œ ๋”ฐ์˜จ๊ฒƒ๋“ค์ž…๋‹ˆ๋‹ค. ํ•˜์ง€๋งŒ ๋น„๋ฐ€๋ฒˆํ˜ธ๊ฐ€ ์˜ฌ๋ฐ”๋ฅธ์ง€ ํ™•์ธํ•˜๋Š” ๋ถ€๋ถ„์ด ์ถ”๊ฐ€๋˜์—ˆ์Šต๋‹ˆ๋‹ค (์ง€๋‚œ๋ฒˆ์—๋Š” ํ•ญ์ƒ ์ฐธ์ด์—ˆ์ฃ ?). ์‚ฌ์šฉ์ž๊ฐ€ ์ž…๋ ฅํ•œ ๋น„๋ฐ€๋ฒˆํ˜ธ๋ฅผ ์ €์žฅ๋˜์–ด ์žˆ๋˜ ์†Œ๊ธˆ๊ฐ’๊ณผ ํ•ฉํ•˜์—ฌ ํ•ด์‰ฌํ•˜๊ณ , ์ด๋ฅผ ๊ธฐ์กด์˜ ํ•ด์‰ฌ๋œ ๋น„๋ฐ€๋ฒˆํ˜ธ์™€ ๋น„๊ตํ•ฉ๋‹ˆ๋‹ค.

๋งŒ์•ฝ ๋กœ๊ทธ์ธ ์•„์ด๋””์™€ ๋น„๋ฐ€๋ฒˆํ˜ธ๊ฐ€ ์ •ํ™•ํ•˜๋‹ค๋ฉด, ํผ๊ฐ’ ํ™•์ธ ํด๋ž˜์Šค๋Š” ์ฐธ ์„ ๋ฐ˜ํ™˜ํ•˜๊ณ  ์›๋ž˜์˜ ์•ก์…˜์ธ executeLogin() ์ด ์‹คํ–‰๋  ๊ฒƒ์ž…๋‹ˆ๋‹ค. ๋งŒ์•ฝ ๊ทธ๋ ‡์ง€ ์•Š๋‹ค๋ฉด ๊ฑฐ์ง“ ์„ ๋ฐ˜ํ™˜ํ•˜๊ณ , handleErrorLogin() ๋ฉ”์˜๋“œ๊ฐ€ ์‹คํ–‰๋  ๊ฒƒ์ž…๋‹ˆ๋‹ค.

์•ก์…˜์—์„œ ์ฝ”๋“œ ์ œ๊ฑฐํ•˜๊ธฐ

์ž ์ด์ œ ์‚ฌ์šฉ์ž๋ฅผ ํ™•์ธํ•˜๋Š” ์ฝ”๋“œ๋Š” ํผ๊ฐ’ ํ™•์ธ ํด๋ž˜์Šค๋กœ ์˜ฎ๊ฒจ์กŒ์œผ๋‹ˆ, ๊ธฐ์กด ์ฝ”๋“œ๋ฅผ login ์•ก์…˜์—์„œ ์ œ๊ฑฐํ•˜๋„๋ก ํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค. ์•ก์…˜์ด POST ๋ฉ”์˜๋“œ๋กœ ํ˜ธ์ถœ๋˜๋Š” ๊ฒฝ์šฐ, ์‚ฌ์šฉ์ž์˜ ์š”์ฒญ์€ ํผ๊ฐ’ ํ™•์ธ ๋ชจ๋“ˆ๋กœ๋ถ€ํ„ฐ ํผ๊ฐ’์ด ์˜ฌ๋ฐ”๋ฅธ์ง€ ํ™•์ธ์„ ๋ฐ›๊ฒŒ ๋˜๋ฏ€๋กœ executeLogin() ๊ฐ€ ์‹คํ–‰๋˜์—ˆ๋‹ค๋ฉด, ํ•ด๋‹น ์‚ฌ์šฉ์ž์˜ ์•„์ด๋””์™€ ๋น„๋ฐ€๋ฒˆํ˜ธ๋Š” ์ •ํ™•ํ•˜๋‹ค๊ณ  ๋ณผ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๋”ฐ๋ผ์„œ executeLogin() ๋ฉ”์˜๋“œ์—์„œ๋Š” referer ํŽ˜์ด์ง€๋กœ ์‚ฌ์šฉ์ž๋ฅผ ์ด๋™์‹œํ‚ค๊ธฐ๋งŒ ํ•˜๋ฉด ๋ฉ๋‹ˆ๋‹ค.

public function executeLogin()
{
  if ($this->getRequest()->getMethod() != sfRequest::POST)
  {
    // display the form
    $this->getRequest()->getParameterHolder()->set('referer', $this->getRequest()->getReferer());
 
    return sfView::SUCCESS;
  }
  else
  {
    // handle the form submission
    // redirect to last page
    return $this->redirect($this->getRequestParameter('referer', '@homepage'));
  }
}

ํ…Œ์ŠคํŠธ ์‚ฌ์šฉ์ž์ค‘ ํ•˜๋‚˜์˜ ์ •๋ณด๋ฅผ ์ด์šฉํ•ด์„œ ๋กœ๊ทธ์ธ์„ ์‹œ๋„ํ•ด๋ณด์‹ญ์‹œ์˜ค (์ž๋™์œผ๋กœ ์ ์žฌ๋˜์–ด์•ผํ•˜๋Š” ํผ๊ฐ’ ํ™•์ธ ํด๋ž˜์Šค๊ฐ€ ์ƒ์„ฑ๋˜์—ˆ๊ธฐ ๋•Œ๋ฌธ์—, ์‹คํ–‰์ „์— ์บ์‹œ๋ฅผ ์ง€์šฐ์…”์•ผ ํ•ฉ๋‹ˆ๋‹ค).

์ด์šฉ ์ œํ•œ

ํŠน์ • ์•ก์…˜์— ๋Œ€ํ•ด ์ด์šฉ์„ ์ œํ•œํ•˜๊ณ  ์‹ถ์œผ์‹œ๋‹ค๋ฉด ๋ชจ๋“ˆ์˜ config/ ๋””๋ ‰ํ† ๋ฆฌ์— security.yml ํŒŒ์ผ์„ ์ถ”๊ฐ€ํ•˜์…”์„œ ๋‹ค์Œ๊ณผ ๊ฐ™์ด ๋‚ด์šฉ์„ ์ฑ„์šฐ์…”์•ผ ํ•ฉ๋‹ˆ๋‹ค.

all:
  is_secure:   on
  credentials: subscriber

์ด๋ ‡๊ฒŒ ํ•  ๊ฒฝ์šฐ ํ•ด๋‹น ๋ชจ๋“ˆ์€ ์‚ฌ์šฉ์ž๊ฐ€ ์ธ์ฆ๋œ ์‚ฌ์šฉ์ž์ด๊ณ  subscriber ๋ผ๋Š” ์ฆ๋ช…๊ฐ’ (credential) ์„ ๊ฐ€์ง€๊ณ  ์žˆ๋Š” ๊ฒฝ์šฐ์— ํ•œํ•ด ์ด์šฉํ•  ์ˆ˜ ์žˆ๊ฒŒ๋ฉ๋‹ˆ๋‹ค.

askeet ์—์„œ๋Š” ์ƒˆ๋กœ์šด ์งˆ๋ฌธ์„ ํ•˜๊ฑฐ๋‚˜, ์งˆ๋ฌธ์— ๋Œ€ํ•œ ํฅ๋ฏธ๋„๋ฅผ ์ถ”๊ฐ€ํ•˜๊ฑฐ๋‚˜, ๋˜๋Š” ๋‹ต๋ณ€์— ์ ์ˆ˜๋ฅผ ๋งค๊ธธ๋•Œ์—๋งŒ ๋กœ๊ทธ์ธ์ด ํ•„์š”ํ•˜๋„๋ก ํ•  ๊ฒƒ์ž…๋‹ˆ๋‹ค. ๊ทธ ์™ธ์˜ ๋‹ค๋ฅธ ๋ชจ๋“  ์•ก์…˜์€ ๋กœ๊ทธ์ธ๋œ ์‚ฌ์šฉ์ž๊ฐ€ ์•„๋‹ˆ์–ด๋„ ์ด์šฉ์ด ๊ฐ€๋Šฅํ•  ๊ฒƒ์ž…๋‹ˆ๋‹ค.

์˜ˆ๋ฅผ๋“ค์–ด, question/add ์•ก์…˜์— ๋Œ€ํ•ด์„œ (์•„์ง ์ž‘์„ฑ๋˜์ง€ ์•Š์•˜์ง€๋งŒ) ์ด์šฉ์„ ์ œํ•œํ•˜๊ณ  ์‹ถ๋‹ค๋ฉด, askeet/apps/frontend/modules/question/config/ ๋””๋ ‰ํ† ๋ฆฌ์˜ security.yml ํŒŒ์ผ์„ ๋‹ค์Œ๊ณผ ๊ฐ™์ด ๋งŒ๋“ค๋ฉด ๋ฉ๋‹ˆ๋‹ค.

add:
  is_secure:   on
  credentials: subscriber

all:
  is_secure:   off

๋ฆฌํŽ™ํ† ๋ง ํ•œํŒ?

์ž ๋งˆ์ง€๋ง‰์œผ๋กœ ๋๋‚ด๊ธฐ ์ „์—, ์šฐ๋ฆฌ๊ฐ€ ๊ฐ€์žฅ ์ข‹์•„ํ•˜๋Š” ์ฝ”๋“œ๋ฅผ์˜ฌ๋ฐ”๋ฅธ๊ณณ์—์˜ฎ๊ธฐ๊ธฐ ๊ฒŒ์ž„์„ ํ•œ ํŒ ํ•˜๋„๋ก ํ•˜์ฃ .

๋น„๋ฐ€๋ฒˆํ˜ธ๋ฅผ ํ™•์ธํ•˜๊ณ  ์‚ฌ์šฉ์ž๊ฐ€ ํ™•์ธ๋˜์—ˆ์„๋•Œ, ์‚ฌ์šฉ์ž์˜ ๊ถŒํ•œ์„ ์ฆ๋ช…ํ•˜๊ณ  ๋‚˜์ค‘์„ ์œ„ํ•ด ์‚ฌ์šฉ์ž์˜ id ๋ฅผ ์ €์žฅํ•˜๋Š” ๋„ค์ค„์˜ ์ฝ”๋“œ๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค. ์ด ์ฝ”๋“œ๋“ค์„ myUser ํด๋ž˜์Šค (๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์˜ User ์— ๋Œ€ํ•œ ํด๋ž˜์Šค๊ฐ€ ์•„๋‹ˆ๋ผ ์„ธ์…˜์— ๊ด€ํ•œ ํด๋ž˜์Šค) ์˜ ๋ฉ”์˜๋“œ๋กœ ๋งŒ๋“ค ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. askeet/apps/frontend/lib/myUser.php ํด๋ž˜์Šค์— ๋‹ค์Œ ๋ฉ”์˜๋“œ๋ฅผ ์ถ”๊ฐ€ํ•ฉ๋‹ˆ๋‹ค.

public function signIn($user)
{
  $this->setAttribute('subscriber_id', $user->getId(), 'subscriber');
  $this->setAuthenticated(true);
 
  $this->addCredential('subscriber');
  $this->setAttribute('nickname', $user->getNickname(), 'subscriber');
}
 
public function signOut()
{
  $this->getAttributeHolder()->removeNamespace('subscriber');
 
  $this->setAuthenticated(false);
  $this->clearCredentials();
}

์ด์ œ myLoginValidator ํด๋ž˜์Šค์˜ $this->getContext()->getUser() ๋กœ ์‹œ์ž‘ํ•˜๋Š” ๋„ค ์ค„์„ ์•„๋ž˜์™€ ๊ฐ™์ด ์ˆ˜์ •ํ•ฉ๋‹ˆ๋‹ค.

$this->getContext()->getUser()->signIn($user);

user/logout ์•ก์…˜ ์—ญ์‹œ (์žŠ์œผ์‹ ๊ฑด ์•„๋‹ˆ์ฃ ?) ์•„๋ž˜์™€ ๊ฐ™์ด ์ˆ˜์ •ํ•ฉ๋‹ˆ๋‹ค.

public function executeLogout()
{
  $this->getUser()->signOut();
 
  $this->redirect('@homepage');
}

subscriber_id ์™€ nickname ์„ธ์…˜๊ฐ’๋“ค ์—ญ์‹œ ๊ฒŒํ„ฐ ๋ฉ”์˜๋“œ๋กœ ์ถ”์ƒํ™”๋  ์ˆ˜ ์žˆ์„ ๊ฒƒ์ž…๋‹ˆ๋‹ค. myUser ํด๋ž˜์Šค์— ๋‹ค์Œ ์„ธ ๋ฉ”์˜๋“œ๋“ค์„ ์ถ”๊ฐ€ํ•ฉ๋‹ˆ๋‹ค.

public function getSubscriberId()
{
  return $this->getAttribute('subscriber_id', '', 'subscriber');
}
 
public function getSubscriber()
{
  return UserPeer::retrieveByPk($this->getSubscriberId());
}
 
public function getNickname()
{
  return $this->getAttribute('nickname', '', 'subscriber');
}

layout.php ์—์„œ๋„ ์ƒˆ๋กœ์šด ๋ฉ”์˜๋“œ๋“ค์„ ์‚ฌ์šฉํ•˜๋„๋ก ํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค. ์•„๋ž˜ ์ฝ”๋“œ๋ฅผ

<li><?php echo link_to($sf_user->getAttribute('nickname', '', 'subscriber').' profile', 'user/profile') ?></li>

์•„๋ž˜์™€ ๊ฐ™์ด ๋ฐ”๊พธ์‹ญ์‹œ์˜ค.

<li><?php echo link_to($sf_user->getNickname().' profile', 'user/profile') ?></li>

์ˆ˜์ •์‚ฌํ•ญ์„ ํ…Œ์ŠคํŠธํ•˜๋Š” ๊ฒƒ์„ ์žŠ์ง€ ๋งˆ์‹ญ์‹œ์˜ค. ์ด์ „๊ณผ ๋กœ๊ทธ์ธ ํ”„๋กœ์„ธ์Šค๊ฐ€ ๋™์ผํ•˜๊ฒŒ ์ฒ˜๋ฆฌ๋˜์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค (ํ•˜์ง€๋งŒ ์ฝ”๋“œ๋Š” ํ›จ์”ฌ ๊น”๋”ํ•˜์ฃ ).

๋‚ด์ผ ์ด์‹œ๊ฐ„์—

๋‚ด์ผ์€ ๋ทฐ ์„ค์ •์„ ์‚ดํŽด๋ณด๊ณ , CSS ๋ฅผ ์ˆ˜์ •ํ•˜๊ณ , ์ผ๊ด€๋œ (consistent) ์ปดํฌ๋„ŒํŠธ๋“ค๊ณผ๊ณ  ํŽ˜์ด์ง€ ํ—ค๋”๋ฅผ ์‚ดํŽด๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค.

์˜ค๋Š˜ ์ž‘์„ฑ๋œ ์ฝ”๋“œ๋“ค์€ askeet SVN ์ €์žฅ์†Œ ์—์„œ release_day_6 ํƒœ๊ทธ๋กœ ๋‹ค์šด๋กœ๋“œ ๊ฐ€๋Šฅํ•ฉ๋‹ˆ๋‹ค. askeet ์— ๋Œ€ํ•œ ์งˆ๋ฌธ์€ askeet ํฌ๋Ÿผ ์—์„œ ์ž์œ ๋กญ๊ฒŒ ํ•ด์ฃผ์‹œ๊ธฐ ๋ฐ”๋ž๋‹ˆ๋‹ค. 21์ผ์งธ ๋ฌด์—‡์„ ํ• ์ง€๋Š” ์—ฌ๋Ÿฌ๋ถ„์—๊ฒŒ ๋‹ฌ๋ ค ์žˆ๋‹ค๋Š” ๊ฒƒ๋„ ์žŠ์ง€ ๋ง์•„์ฃผ์‹œ๊ตฌ์š”.

This work is licensed under the Creative Commons Attribution-Noncommercial-No Derivative Works 3.0 Unported License license.