๐Ÿ”• ๋ฐฐ๊ฒฝ ์ƒํ™ฉ

์ง€๊ธˆํ•˜๋Š” ํ”„๋กœ์ ํŠธ์—๋Š” ๋ฉ”์ผ ๊ตฌ๋… ์„œ๋น„์Šค ๊ธฐ๋Šฅ์ด ์žˆ๋‹ค. ๊ฐœ์ธ์ •๋ณด๋ณดํ˜ธ ํŒ€์—์„œ ํ•ด๋‹น ๊ธฐ๋Šฅ ๊ฒ€ํ†  ํ›„ ๋ณด์™„ ์š”์ฒญ์ด ๋“ค์–ด์™”๋‹ค. ๊ธฐ๋Šฅ ์š”๊ฑด๊ณผ ๋ณด์™„ ์š”๊ฑด์„ ๊ฐ™์ด ๋‚˜์—ดํ•˜๋ฉด ๋‹ค์Œ๊ณผ ๊ฐ™๋‹ค.

  1. ๊ตฌ๋…์ทจ์†Œ ๋ฐ ์ˆ˜์‹ ์ฐจ๋‹จ ๊ธฐ๋Šฅ์ด ์กด์žฌ ํ•ด์•ผํ•œ๋‹ค.
  2. ๊ตฌ๋… ์ทจ์†Œ ๋ฉ”์ผ ์ฃผ์†Œ๋ฅผ ๋‚ด๋ถ€์—์„œ ๊ด€๋ฆฌํ•˜๋ฉด ์•ˆ๋œ๋‹ค.
  3. ๊ด‘๊ณ ์„ฑ/์ •๋ณด์„ฑ ๋ฉ”์ผ์„ ๊ตฌ๋ถ„ํ•ด์„œ ๊ด€๋ฆฌํ•ด์•ผํ•œ๋‹ค. ์ฆ‰ ๊ด‘๊ณ ์„ฑ ์ •๋ณด ์ˆ˜์‹  ์ฐจ๋‹จ๊ณผ ๋ชจ๋“  ๋ฉ”์ผ ์ˆ˜์‹  ์ฐจ๋‹จ์€ ๊ตฌ๋ถ„๋˜์–ด์•ผ ํ•œ๋‹ค.
  4. ํ•ด๋‹น ํ”„๋กœ์ ํŠธ๋Š” ์‚ฌ์šฉ์ž, ์ฆ‰ ๋กœ๊ทธ์ธ์ด ์กด์žฌํ•˜์ง€ ์•Š๊ธฐ ๋•Œ๋ฌธ์— ์‚ฌ์šฉ์ž์˜ ์ˆ˜์‹ ์ฐจ๋‹จ ์„ค์ •์€ ์—†๋‹ค.

ํ•ด๋‹น ์š”๊ฑด์„ ๋งŒ์กฑํ•˜๊ธฐ ์œ„ํ•ด ์—ฌ๋Ÿฌ๊ฐ€์ง€ ์‹œ๋„๋ฅผ ํ–ˆ๋Š”๋ฐ, ์ด ๋ฐฉ๋ฒ•๋“ค ์ค‘ ์–ด๋–ค ๋ฐฉ๋ฒ•์ด ๋‹ค๋ฅธ ์‚ฌ๋žŒ์—๊ฒŒ ๋„์›€์ด ๋  ์ˆ˜๋„ ์žˆ์ง€ ์•Š์„๊นŒ ํ•ด์„œ ๊ณต์œ  ๋ชฉ์ ์œผ๋กœ ๊ธ€์„ ์“ฐ๊ธฐ๋กœ ๋งˆ์Œ ๋จน์—ˆ๋‹ค.

 

1๏ธโƒฃ ์ฒซ ๋ฒˆ์งธ ์‹œ๋„

์ฒซ ๋ฒˆ์งธ ์‹œ๋„์˜ ์˜๋„๋Š” ์–ดํ”Œ๋ฆฌ์ผ€์ด์…˜์—์„œ ๋ฉ”์ผ ์ˆ˜์‹ ์ฐจ๋‹จ ๊ธฐ๋Šฅ์„ ์‹ ๊ฒฝ ์“ฐ๊ณ  ์‹ถ์ง€ ์•Š๋‹ค๋Š” ๊ฒƒ์ด๋‹ค. ๋‚ด๋ถ€ DB์—์„œ ์ฐจ๋‹จ ๋ชฉ๋ก๊ณผ ๊ฐ™์ด ์ฐจ๋‹จํ•œ ์‚ฌ์šฉ์ž์˜ ์ด๋ฉ”์ผ์„ ๊ด€๋ฆฌํ•˜๋ฉด ์•ˆ๋˜๊ธฐ ๋•Œ๋ฌธ์— ์‚ฌ์šฉํ•˜๊ณ  ์žˆ๋Š” ๋ฉ”์ผ Third-Party์ธ SendGrid์—์„œ ๊ด€๋ฆฌํ•˜๋Š” ๊ฒƒ์ด ๋ชฉ์ ์ด๋‹ค.

 

    1. sendgrid ์„ค์ • ํŽ˜์ด์ง€์—์„œ Unsubscribe Groups ๋ฅผ ์ถ”๊ฐ€ํ•œ๋‹ค

    2. Subscription Tracking ๊ธฐ๋Šฅ์„ OFF ๋กœ ์„ค์ •ํ•œ๋‹ค.

 

    3. sendgrid์˜ send API์—์„œ asm ํ•„๋“œ๋ฅผ ์ถ”๊ฐ€ํ•œ๋‹ค. [์ฐธ๊ณ  ๋ฌธ์„œ]

 

val asm = ASM().apply {
	groupId = 33868
}

val mail = Mail().apply {
	this.from = from
	this.subject = "local-test"
	this.content = mutableListOf(content)
	this.asm = asm
}

val sendGrid = SendGrid(sendGridKey)
val request = Request().apply {
	method = Method.POST
	endpoint = "mail/send"
	body = mail.build()
}

val response = sendGrid.api(request)

 

    4. mail message์— unsubscribe์™€ global-unsubscribe ํƒœ๊ทธ๋ฅผ ์ถ”๊ฐ€ํ•œ๋‹ค. [์ฐธ๊ณ  ๋ฌธ์„œ]

<a href='<%asm_unsubscribe_raw_url%>'>๊ตฌ๋…์ทจ์†Œ</a>
<a href='<%asm_global_unsubscribe_raw_url%>'>์ˆ˜์‹ ๊ฑฐ๋ถ€</a>

 

์ด๋ ‡๊ฒŒ ์„ค์ •ํ•˜๋ฉด 1๋ฒˆ ๋‹จ๊ณ„์—์„œ ๋งŒ๋“  group์„ ๊ด‘๊ณ ์„ฑ ๋ฉ”์ผ ์ˆ˜์‹ ์ฐจ๋‹จ ๊ทธ๋ฃน์œผ๋กœ, global ์ˆ˜์‹ ์ฐจ๋‹จ ๊ทธ๋ฃน์„ ๋ชจ๋“  ๋ฉ”์ผ ์ˆ˜์‹  ์ฐจ๋‹จ ๊ทธ๋ฃน์œผ๋กœ ๊ด€๋ฆฌํ•  ์ˆ˜ ์žˆ๋‹ค.

 

๊ฒฐ๊ณผ๋ฌผ์€ ์•„๋ž˜ ๊ทธ๋ฆผ์ด๋‹ค.

์™ผ์ชฝ์€ ์ˆ˜์‹ ์ฐจ๋‹จ ๊ธฐ๋Šฅ, ์˜ค๋ฅธ์ชฝ์€ ๊ด‘๊ณ ์„ฑ ๋ฉ”์ผ ์ฐจ๋‹จ ๊ธฐ๋Šฅ์ด๋‹ค.

 

ํ•ด๋‹น ์ด๋ฉ”์ผ ํ…œํ”Œ๋ฆฟ์„ ๋ณ€๊ฒฝ ํ•ด๋ณด๋ ค๊ณ  ์‹œ๊ฐ„์„ ๊ฝค ์ผ์ง€๋งŒ, sendgrid์—์„œ ๋งŒ๋“ค์–ด์„œ ๋ณด๋‚ด์ฃผ๋Š” ๊ฒƒ์ด๋ผ ์ปค์Šคํ…€ํ•˜๊ฒŒ ์„ค์ •ํ•  ์ˆ˜ ์—†๋‹ค. ์•„๋ฌด๋ž˜๋„ B2C ์„œ๋น„์Šค์ด๋‹ค ๋ณด๋‹ˆ ๋„ˆ๋ฌด ๋ชป์ƒ๊ธฐ๊ณ  ์˜์–ด๋กœ ๋˜์–ด ์žˆ์–ด์„œ ์„ ํƒ ๋ฐ›์ง€ ๋ชปํ•˜์˜€๋‹ค.

 

2๏ธโƒฃ ๋‘ ๋ฒˆ์งธ ์‹œ๋„

๋‘ ๋ฒˆ์งธ ์‹œ๋„๋ถ€ํ„ฐ๋Š” ์ˆ˜์‹  ๋™์˜์ž๋ฅผ DB์—์„œ ๊ด€๋ฆฌํ•˜๋Š” ๊ฒƒ์ด ๊ธฐ๋ณธ ์ „์ œ์ด๋‹ค. ์ด๋ ‡๊ฒŒ ์ •ํ•œ ์ด์œ ๋Š” ๊ฐœ์ธ์ •๋ณด๋ณดํ˜ธ ํŒ€์—์„œ ์จ๋“œํŒŒํ‹ฐ์—์„œ ๊ด€๋ฆฌ๋˜๋Š” ์ด๋ฉ”์ผ๋„ ์กด์žฌํ•ด์„œ๋Š” ์•ˆ๋œ๋‹ค๊ณ  ํ–ˆ๊ธฐ ๋•Œ๋ฌธ์ด๋‹ค. ๊ทธ๋ž˜์„œ ์ˆ˜์‹ ์ฐจ๋‹จ ์ด๋ฉ”์ผ์„ ๊ด€๋ฆฌํ•˜์ง€ ์•Š๊ณ , ์ˆ˜์‹ ๋™์˜ํ•œ ์ด๋ฉ”์ผ์„ ๊ด€๋ฆฌํ•˜๋Š” ๊ฒƒ์„ ์„ ํƒํ•˜์˜€๋‹ค. (์ˆ˜์‹ ๋™์˜์ž๋Š” ์ด๋ฉ”์ผ ์ˆ˜์ง‘์„ ์„ ํƒํ•˜์˜€๊ธฐ ๋•Œ๋ฌธ์— ๋ฒ•์  ๋ฌธ์ œ๊ฐ€ ์—†๋‹ค.)

 

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

 

๊ทธ๋Ÿผ ์ˆ˜์‹ ์ฐจ๋‹จ์ด ๋ฌธ์ œ์ธ๋ฐ, ๊ด‘๊ณ ์„ฑ ์ •๋ณด ์ˆ˜์‹  ์ฐจ๋‹จ์€ API๋ฅผ ํ˜ธ์ถœํ•˜์—ฌ ์ˆ˜์‹  ๋™์˜์ž๋ฅผ ์ง€์šฐ๋Š” ๋ฐฉ๋ฒ•์„ ์„ ํƒํ•˜์˜€๊ณ , ๋ชจ๋“  ๋ฉ”์ผ ์ˆ˜์‹  ์ฐจ๋‹จ ๊ธฐ๋Šฅ์€ ์ œ๊ฑฐํ•˜๊ธฐ๋กœ ํ•˜์˜€๋‹ค. ์‚ฌ์šฉ์ž๊ฐ€ ์ŠคํŒธ ๋ฉ”์ผํ•จ์œผ๋กœ ๋ณด๋‚ด๋Š” ๊ฒƒ๊ณผ ํฌ๊ฒŒ ๋‹ค๋ฅด์ง€ ์•Š์•„ ์˜๋ฏธ๊ฐ€ ์—†๋‹ค๊ณ  ํŒ๋‹จํ•˜์˜€๋‹ค. (๋ฉ”์ผ ์†ก์‹ ์ž์˜ ํ‰๊ฐ€ ์ ์ˆ˜๊ฐ€ ๋–จ์–ด์งˆ ์ˆ˜ ์žˆ๋‹ค๊ณ  ๊ณต์œ ํ–ˆ์ง€๋งŒ ์‹ ๊ฒฝ ์•ˆ์“ด๋‹ค๊ณ  ํ–ˆ๋‹ค...!)

 

๋‘ ๋ฒˆ์งธ ์‹œ๋„์˜ ๋ชฉ์ ์€ ์‚ฌ์šฉ์ž๊ฐ€ ๋งํฌ๋งŒ ๋ˆ„๋ฅด๋ฉด ์ž๋™์œผ๋กœ ์ˆ˜์‹ ์ฐจ๋‹จ์ด ๋˜๋„๋ก ํ•˜๋Š” ๊ฒƒ์ด๋‹ค.

 

    1. ์˜ˆ์•ฝ๋œ ๋Œ€์ฒด๋ฌธ์ž ์‚ฌ์šฉ

 

sendgrid๊ฐ€ ์‚ฌ์šฉ์ž ๋ณ„๋กœ ๊ตฌ๋…์ทจ์†Œ ์‹œ ๋‹ค๋ฅธ url์„ ์ค„ ์ˆ˜ ์žˆ๋‹ค๋Š” ๊ฒƒ(์ฒซ ๋ฒˆ์งธ ์‹œ๋„ ์ฐธ๊ณ )์€ ์ด๋ฉ”์ผ ๋ณธ๋ฌธ์— ์‚ฌ์šฉ์ž ์ด๋ฉ”์ผ์„ ๋„ฃ์„ ์ˆ˜ ์žˆ๋‹ค๊ณ  ํŒ๋‹จํ•˜์˜€๋‹ค.

send API ์‚ฌ์šฉ ์‹œ personalizations ๊ฐ’์„ ๊ฐ™์ด ์ „์†กํ•˜์—ฌ ์‚ฌ์šฉ์ž ๋ณ„๋กœ ๋‹ค๋ฅธ ๊ฐ’์„ ์ „๋‹ฌํ•  ์ˆ˜ ์žˆ๋‹ค. substitution์„ ์‚ฌ์šฉํ•ด์„œ ๋Œ€์ฒด๋ฌธ์ž๋„ ๊ฐ€๋Šฅํ•˜๋‹ค. [๊ด€๋ จ ๋ฌธ์„œ]

val personalizations = toEmails.map {
    Personalization().apply {
        addTo(Email(it))
        addSubstitution(":email:", it)
    }
}

val mail = Mail().apply {
    this.from = from
    this.subject = "local-test"
    this.content = mutableListOf(content)
    this.personalization = personalizations
}

 

    2. mail message์— ๋Œ€์ฒด๋ฌธ์ž ์‚ฝ์ž… ํ›„ ์ „์†ก

<a href='https:/test.com/?email=:email:'>๊ตฌ๋…์ทจ์†Œ</a>
:email: ์ด ๋งž์Šต๋‹ˆ๊นŒ?

 

๊ฒฐ๊ณผ๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™๋‹ค.

์ข€ ๋” ์ด์˜๊ฒŒ ๊พธ๋ฏธ๋ฉด ์ด๋Œ€๋กœ ์‚ฌ์šฉํ•ด๋„ ๋  ๊ฒƒ ๊ฐ™๋‹ค. ๋งŒ์•ฝ email์ด url์— query string์œผ๋กœ ๋…ธ์ถœ๋˜๋Š” ๊ฒƒ์ด ๊ฑฑ์ •๋œ๋‹ค๋ฉด ๋ฒ„ํŠผ์„ ๋งŒ๋“ค์–ด์„œ POST ์š”์ฒญ์œผ๋กœ request body์— ๋„ฃ์–ด์„œ ๋ณด๋‚ด๋„๋ก ์„ค์ •ํ•˜๋ฉด ๋œ๋‹ค.

 

๐Ÿ”” ๋งˆ๋ฌด๋ฆฌ

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

 

๋น„๋ก ๋ฉ‹์ง€๊ฒŒ ๊ธฐ๋Šฅ์„ ๋งŒ๋“ค์ง„ ๋ชปํ–ˆ์ง€๋งŒ, ์ด ์—ฌ์ • ์ค‘์— sendgrid ๊ธฐ๋Šฅ์— ๋Œ€ํ•ด ์ฐพ๊ธฐ ์–ด๋ ต๊ณ  ํŒŒํŽธํ™” ๋œ ์ •๋ณด๋ฅผ ์–ด๋”˜๊ฐ€ ๋ชจ์•„ ์ฃผ๋ฉด ์ข‹๊ฒ ๋‹ค๋ผ๋Š” ์ƒ๊ฐ์„ ํ•˜์˜€๋‹ค. ๊ทธ๋ž˜์„œ ์ด๋ ‡๊ฒŒ ๋ธ”๋กœ๊ทธ ๊ธ€์ด๋ผ๋„ ์จ์„œ ๋‚จ๊ฒจ๋‘๊ธฐ๋กœ ํ–ˆ๋‹ค. ๋!

+ Recent posts