Skip to content

teambaekgu/baekgu_dev

Folders and files

NameName
Last commit message
Last commit date

Latest commit

ย 

History

233 Commits
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 

Repository files navigation

๋ฐฑ๊ตฌ : ํ—ฌ์„œ๋“ค์„ ์œ„ํ•œ ๋‹จ๋ฐฑ์งˆ ๋ณด์ถฉ ์‹๋‹จ ์ถ”์ฒœ ์„œ๋น„์Šค

image

๐Ÿ—‚๏ธํ”„๋กœ์ ํŠธ ์†Œ๊ฐœ

๐Ÿ“ƒ์ฃผ์ œ ์ •์˜ ๋ฌธ์„œ

๐Ÿพ ๊ธฐํš ์˜๋„image

MZ์„ธ๋Œ€์—์„œ ํŠธ๋ Œ๋“œ ํ‚ค์›Œ๋“œ๋กœ ๋ฝ‘ํžˆ๋Š” #์šด๋™ #์ž๊ธฐ๊ด€๋ฆฌ

  • Instagram์˜ #์˜ค์šด์™„(์˜ค๋Š˜์šด๋™์™„๋ฃŒ)์ธ์ฆ๊ธ€์€ 385๋งŒ๊ฐœ ํ—ฌ์Šค๋ฅผ ์ฃผ์ œ๋กœ ํ•˜๋Š” ์œ ํŠœ๋ฒ„๋“ค์˜ ๊ตฌ๋…์ž๊ฐ€ 100๋งŒ๋ช…์ด ๋„˜๋Š” ๋“ฑ SNS์™€ ํ”Œ๋žซํผ์œผ๋กœ MZ์„ธ๋Œ€๊ฐ€ ๋งŽ์€ ์ž๊ทน์„ ๋ฐ›์Œ
  • '์ž๊ธฐ๊ด€๋ฆฌ = ๊ฑด๊ฐ„๊ด€๋ฆฌ' ๋ผ๋Š” ์‚ฌํšŒ์  ๋ถ„์œ„๊ธฐ๊ฐ€ ํ˜•์„ฑ๋˜๋ฉด์„œ "๋ค๋ฒจ ์ด์ฝ”๋…ธ๋ฏธ" ์‹œ๋Œ€ ์ฆ‰, ๊ฑด๊ฐ•๊ณผ ์šด๋™, ์ฒด๋ ฅ ๊ด€๋ฆฌ์— ๋Œ€ํ•œ ๊ด€์‹ฌ์ด ์ปค์ง€๋ฉด์„œ ๊ฑด๊ฐ• ๊ด€๋ จ ์‹œ์žฅ์ด ๊ธ‰์„ฑ์žฅํ•˜๋Š” ๊ฒฝ์ œ ํ˜„์ƒ์ด๋‚˜ ์†Œ๋น„ ํŒจํ„ด์ด ์ด๋ค„์ง€๊ณ  ์žˆ๋‹ค.
image image
"๋Œ€ํ•™๋‚ด์ผ ํ†ต๊ณ„์ž๋ฃŒ" ์ธ์Šคํƒ€ #์˜ค์šด์™„ ์ธ์ฆ๊ธ€ ํ—ฌ์Šค ์œ ํŠœ๋ฒ„ "ํ”ผ์ง€์ปฌ ๊ฐค๋Ÿฌ๋ฆฌ"

๋‹ค์ด์–ดํŠธ ์•ฑ์€ ๋„˜์ณ๋‚˜์ง€๋งŒ ํ—ฌ์„œ๋ฅผ ์œ„ํ•œ ์‹๋‹จ ๋งž์ถค ์•ฑ์˜ ๋ถ€์žฌ

  • ํ—ฌ์Šค์˜ ์ธ๊ธฐ๊ฐ€ ๋†’์•„์ง€๋ฉด์„œ ๋‹จ๋ฐฑ์งˆ ์‹๋‹จ๋„ ํฌ๊ฒŒ ๊ฐ๊ด‘์„ ๋ฐ›๊ณ  ์žˆ๋‹ค.
  • ๊ทธ๋Ÿฌ๋‚˜ ์‹๋‹จ๊ด€๋ฆฌ ์•ฑ์ด ๋„˜์ณ๋‚˜๋„ ์ „๋ฐ˜์ ์ธ ์ดˆ์ ์€ ์ฒด์ค‘๊ฐ๋Ÿ‰์ด์—ˆ๊ณ , ํ—ฌ์„œ๋“ค์—๊ฒŒ ํ•„์š”ํ•œ ๋‹จ๋ฐฑ์งˆ ๋งž์ถค ์‹๋‹จ ์ถ”์ฒœ์•ฑ์€ ๋ถ€์žฌํ•˜๋‹ค๋Š” ๊ฒƒ์„ ์ธ์ง€ํ•˜์˜€๋‹ค.

๐Ÿพ ์•„์ด๋””์–ด ๋‚ด๊ธฐ

๊ทธ๋ž˜์„œ ์šฐ๋ฆฌ๋Š” ์•„๋ž˜์˜ ๊ธฐ๋Šฅ๋“ค์„ ๊ตฌํ˜„ํ•˜์—ฌ ๋‹จ๋ฐฑ์งˆ ์‹๋‹จ ๊ด€๋ฆฌ ํŽธ์„ฑ์— ๋Œ€ํ•œ ๋ฒˆ๊ฑฐ๋กœ์›€๊ณผ ๋‹จ์ผํ•œ ๋‹จ๋ฐฑ์งˆ ์‹ํ’ˆ์— ๋Œ€ํ•œ ๊ถŒํƒœ๊ฐ์„ ํ•ด์†Œ์‹œํ‚ค๋Š”
ํ—ฌ์„œ& ๋‹ค์ด์–ดํ„ฐ๋ฅผ ์œ„ํ•œ๊ฐœ์ธ ๋งž์ถคํ˜• ๋‹จ๋ฐฑ์งˆ ์‹๋‹จ ๊ด€๋ฆฌ ๋ฐ ์ œ๊ณต ์„œ๋น„์Šค๋ฅผ ์ œ๊ณตํ•˜๊ธฐ๋กœ ํ–ˆ๋‹ค.

  1. ์„ค๋ฌธ ์กฐ์‚ฌ๋ฅผ ํ†ตํ•ด ๊ฐœ์ธ์ •๋ณด์™€ ์ƒํ’ˆ์— ๋Œ€ํ•œ ์„ ํ˜ธ๋„์กฐ์‚ฌ๋ฅผ ๋ฐ”ํƒ•์œผ๋กœ ๋‹จ๋ฐฑ์งˆ ์‹๋‹จ์„ ์ถ”์ฒœ, ์ œ๊ณต
  2. ํ•˜๋ฃจ ๋ชฉํ‘œ ๋‹จ๋ฐฑ์งˆ ์„ญ์ทจ์œจ์„ ์ฒดํฌํ•ด ์ฃผ๋Š” ๊ธฐ๋Šฅ ์ œ๊ณต
  3. ์นดํ…Œ๊ณ ๋ฆฌ ๋ณ„ ๊ฐœ๋ณ„ ์ƒํ’ˆ ํ™•์ธ ๊ธฐ๋Šฅ ์ œ๊ณต
๐Ÿ˜ŽํŽ˜๋ฅด์†Œ๋‚˜ image image
๐ŸŽจ์ผ๋Ÿฌ์ŠคํŠธ ๋‹จ๋ฐฑ์งˆ ๊ตฌ๋…์˜ ์ค„์ž„๋ง๋กœ "๋ฐฑ๊ตฌ"๋ผ๋Š” ์ด๋ฏธ์ง€๋ฅผ ๋– ์˜ฌ๋ ธ๊ณ , ์ผ๋Ÿฌ์ŠคํŠธ๋กœ ๊ตฌํ˜„ํ•ด์„œ splashํ™”๋ฉด, ๋กœ๊ทธ์ธ ํ™”๋ฉด, ์•ฑ ์ด๋ฏธ์ง€์— ํ™œ์šฉํ•˜์˜€๋‹ค.
image image image
๐Ÿ–Š๊ฐœ๋ฐœ ๋ฌธ์„œ

๐Ÿ“” ์•ฑ ์„ค๋ช…

๐Ÿ“”ํ…œํ”Œ๋ฆฟ๊ตฌ์กฐ

  • ๋ณธ ์ฝ”๋“œ๋Š” mvp๋””์ž์ธ ํŒจํ„ด์„ ๊ธฐ๋ฐ˜์œผ๋กœ ํ•˜๊ณ ์žˆ๋‹ค
  • ํด๋” ๊ตฌ์กฐ๋Š” ํฌ๊ฒŒ config ํด๋”, src ํด๋”, util ํด๋”๋กœ ์ด๋ฃจ์–ด์ ธ ์žˆ๋‹ค.

config ํด๋”


๊ทผ๊ฐ„์ด ๋˜๋Š” ์ฝ”๋“œ๋“ค์ด ์œ„์น˜ํ•ด ์žˆ๋‹ค.

< ApplicationClass >

์•ฑ์ด ์‹คํ–‰๋  ๋•Œ ๋งจ ์ฒ˜์Œ ์‹คํ–‰๋˜๋Š” ์ฝ”๋“œ๋“ค์ด ์œ„์น˜ํ•ด ์žˆ๋‹ค.
๋”ฐ๋ผ์„œ ๊ฐ์ข… ์ „์—ญ๋ณ€์ˆ˜์™€ ์•ฑ์ด ์‹คํ–‰๋  ๋™์•ˆ ํ•œ ๋ฒˆ๋งŒ ์œ ์ง€๋˜๋ฉด ๋˜๋Š” ๊ฐ์ฒด(์‹ฑ๊ธ€ํ†ค ๊ฐ์ฒด)๋“ค์„ ์ƒ์„ฑํ•˜๋Š” ์ฝ”๋“œ๋“ค์ด ์œ„์น˜ํ•ด ์žˆ๋‹ค.

< BaseActivity & BaseFragment >

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

< XAccessTokenInterceptor >

์ด ๋ถ€๋ถ„์€ ์ฝ”๋“œ๋ฅผ ๋”ฐ๋กœ ๊ฑด๋“œ๋ฆด ํ•„์š”๋Š” ์—†๋Š” ๋ถ€๋ถ„์ด๋‹ค.
๋„คํŠธ์›Œํฌ ํ†ต์‹ ์„ ํ• ๋•Œ, jwtํ† ํฐ ํ—ค๋”์„ค์ • ๋“ฑ๊ณผ ๊ฐ™์ด ๋„คํŠธ์›Œํฌ ํ†ต์‹  ๊ด€๋ จํ•˜์—ฌ ๊ณตํ†ต์ ์œผ๋กœ ์ฒ˜๋ฆฌํ•ด์•ผ ํ•˜๋Š” ๊ฒƒ๋“ค์„ ์„ค์ •ํ•˜๋Š” ์ฝ”๋“œ๊ฐ€ ์œ„์น˜ํ•ด ์žˆ๋‹ค.


src ํด๋”


src ํด๋”๋Š” Config/ ์˜ ๋ฒ ์ด์Šค ์ฝ”๋“œ๋“ค์„ ๊ธฐ๋ฐ˜์œผ๋กœ, Activity๋‚˜ Fragment ๋“ฑ์„ ๋™์ž‘์‹œํ‚ค๋Š”
์ฆ‰ ์•ฑ์„ ๋™์ž‘์‹œํ‚ค๋Š” ๊ตฌ์ฒด์ ์ธ ๊ตฌํ˜„์„ ์ฝ”๋“œ๋“ค์ด ์œ„์น˜ํ•ด ์žˆ๋‹ค.

src ํด๋”๋Š” ๋„๋ฉ”์ธ(ํ™”๋ฉด)๋ณ„๋กœ ํด๋”๊ฐ€ ๋‚˜๋ˆ ์ ธ์žˆ.
src ํด๋”์˜ ๊ตฌ์กฐ๋Š” ๋ฉ”์ธํ™”๋ฉด, ์Šคํ”Œ๋ž˜์‰ฌ ํ™”๋ฉด ๋“ฑ ํฐ ๋„๋ฉ”์ธ๋ณ„๋กœ ๋‚˜๋ˆ ์ ธ์žˆ๊ณ , Fragment๋ณ„ ์ž‘์€ ๋„๋ฉ”์ธ์œผ๋กœ ๋‚˜๋ˆ ์ ธ์žˆ๋‹ค.
์˜ˆ๋ฅผ๋“ค์–ด ๋ฉ”์ธํ™”๋ฉด์ด๋ผ๋Š” ํฐ ๋„๋ฉ”์ธ ํด๋”์•ˆ์—, ํ™ˆ ํ™”๋ฉด๊ณผ ๋‚ด์ •๋ณด ํ™”๋ฉด ๋“ฑ๊ณผ ๊ฐ™์€ ์ž‘์€ ๋„๋ฉ”์ธ ํด๋”๋“ค์ด ์žˆ๋‹ค.

- rv : ViewPager๋‚˜ RecyclerView ์„ค์ •์— ๋Œ€ํ•œ ์ฝ”๋“œ๋“ค์ด ์œ„์น˜ํ•ด ์žˆ๋‹ค.


util ํด๋”


๋ง ๊ทธ๋Œ€๋กœ ๋„๊ตฌ์ฒ˜๋Ÿผ ์‚ฌ์šฉ๋˜๋Š” ์ฝ”๋“œ๋“ค์ด ์œ„์น˜ํ•ด ์žˆ๋‹ค. Dialog , BottomSheet , Firebase ,RecyclerViewSpacing๊ด€๋ จ ํด๋ž˜์Šค๋“ค์ด ์œ„์น˜ํ•ด ์žˆ๋‹ค.

๐Ÿพ Front-End

์‚ฌ์šฉ๋œ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ
com.github.bumptech.glide:glide:4.12.0
androidx.viewpager2:viewpager2:1.0.0
com.github.xabaras:RecyclerViewSwipeDecorator:1.4
de.hdodenhof:circleimageview:3.1.0
com.google.code.gson:gson:2.9.0
com.squareup.retrofit2:converter-gson:2.9.0

[์•ฑ ์ฒ˜์Œ ์‹คํ–‰ ์‹œ]

์•ฑ์„ ์‹คํ–‰ ํ›„ ๋กœ๋”ฉ ๋  ๋•Œ ๋ฐฑ๊ตฌ ์บ๋ฆญํ„ฐ๊ฐ€ ๋‹ด๊ธด splash ํ™”๋ฉด์œผ๋กœ ์‹œ์ž‘ํ•ฉ๋‹ˆ๋‹ค.
์‚ฌ์šฉ์ž๋Š” ๊ณ„์ •์ด ์žˆ๋Š” ๊ฒฝ์šฐ ๋กœ๊ทธ์ธํ•˜์—ฌ ๋ฉ”์ธ ํŽ˜์ด์ง€๋กœ ์ด๋™ํ•˜๊ณ  ์ฒซ ์‚ฌ์šฉ์ž ์ผ ๊ฒฝ์šฐ์—๋Š” ํšŒ์›๊ฐ€์ž…์„ ์ง„ํ–‰ํ•ฉ๋‹ˆ๋‹ค.
ํšŒ์› ๊ฐ€์ž…์€ ์ด๋ฉ”์ผ, ๋น„๋ฐ€๋ฒˆํ˜ธ, ์ด๋ฆ„ ์™ธ ๊ฐœ์ธ ์ •๋ณด๋“ค์„ ์ž…๋ ฅํ•˜๊ณ  ์™„๋ฃŒ ์‹œ ํŒŒ์ด์–ด๋ฒ ์ด์Šค DB๋กœ ์ €์žฅ๋ฉ๋‹ˆ๋‹ค.




[์„ค๋ฌธ ์กฐ์‚ฌ]

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




[๋ฉ”์ธ ํŽ˜์ด์ง€ (ํ”ผ๋“œ)]

๋ฉ”์ธํ™”๋ฉด์— ํ”ผ๋“œ ๋ถ€๋ถ„์—์„œ๋Š” ์นดํ…Œ๊ณ ๋ฆฌ ๋ณ„ ์„œ๋น„์Šค์—์„œ ์ œ๊ณตํ•˜๋Š” ์ƒํ’ˆ๋“ค ๋ชฉ๋ก์„ ํ™•์ธํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
์ƒํ•˜ ์Šคํฌ๋กค์„ ํ†ตํ•ด ์นดํ…Œ๊ณ ๋ฆฌ ์—ด๋žŒ์ด ๊ฐ€๋Šฅํ•˜๊ณ  ์นดํ…Œ๊ณ ๋ฆฌ์—์„œ ์ขŒ์šฐ ์Šคํฌ๋กค ์‹œ ํ•ด๋‹น ์นดํ…Œ๊ณ ๋ฆฌ์˜ ์ œํ’ˆ์„ ํ™•์ธํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.


๋ฉ”์ธํ™”๋ฉด ์† ๊ฐœ๋ณ„ ์ œํ’ˆ์„ ํด๋ฆญํ•˜๋ฉด ํ•ด๋‹น ์ œํ’ˆ์˜ ์˜์–‘ ์„ฑ๋ถ„๊ณผ ์ƒํ’ˆ์ •๋ณด๋ฅผ ํ™•์ธํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.



[๋ฉ”์ธ ํŽ˜์ด์ง€ (์บ˜๋ฆฐ๋”)]

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



[๋ฉ”์ธ ํŽ˜์ด์ง€ (๋งˆ์ดํŽ˜์ด์ง€)]

๋งˆ์ดํŽ˜์ด์ง€ ๋ถ€๋ถ„์—์„œ๋Š” ์‚ฌ์šฉ์ž์˜ ํ”„๋กœํ•„๊ณผ ์‹ ์ฒด์ •๋ณด, ๋ชฉํ‘œ ๋‹จ๋ฐฑ์งˆ๋Ÿ‰์„ ๋ณผ ์ˆ˜ ์žˆ๊ณ  ๊ฐœ์ธ ์ •๋ณด์™€ ์„ค๋ฌธ ์ •๋ณด๋ฅผ ์ˆ˜์ •ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
๊ฐœ์ธ์ •๋ณด ์ˆ˜์ • ๋ฒ„ํŠผ์„ ํด๋ฆญ ์‹œ ํ”„๋กœํ•„ ์ˆ˜์ • ํ™”๋ฉด์ด ๋‚˜ํƒ€๋‚˜๊ณ  ๊ฐœ์ธ์ •๋ณด๋ฅผ ๋ณ€๊ฒฝํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
๋˜ํ•œ ์„ค๋ฌธ์กฐ์‚ฌ ์ •๋ณด ์ˆ˜์ •์„ ํด๋ฆญ ์‹œ ์„ค๋ฌธ์กฐ์‚ฌ๋ฅผ ๋‹ค์‹œ์ง„ํ–‰ํ•˜์—ฌ ์„ค๋ฌธ ์ •๋ณด๋ฅผ ๋ณ€๊ฒฝํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

๐Ÿพ Back-End (Firebase)

1. ์‚ฌ์šฉ์ž ์ •๋ณด

  1. ์ด๋ฉ”์ผ ํ˜•์‹๊ณผ ๋น„๋ฐ€๋ฒˆํ˜ธ ํ˜•์‹(์˜๋ฌธ, ์ˆซ์ž, ํŠน์ˆ˜๋ฌธ์ž ์กฐํ•ฉ), ๋นˆ์นธ ์œ ๋ฌด๋ฅผ ํ™•์ธ ํ›„ ์ด์ƒ์ด ์—†์œผ๋ฉด ํšŒ์›๊ฐ€์ž… ํ•จ์ˆ˜๋ฅผ ์‹คํ–‰ํ•œ๋‹ค.
// ํšŒ์›๊ฐ€์ž… ์™„๋ฃŒ ๋ฒ„ํŠผ
binding.signupTvFinish.setOnClickListener() {
    emailValue = binding.signupEditId.text.toString()
    pwValue = binding.signupEditPw.text.toString()
    nameValue = binding.signupEditName.text.toString()
    birthValue = binding.signupEditBirthday.text.toString()
    phoneValue = binding.signupEditNumber.text.toString()
    addressValue = binding.signupEditTown.text.toString()
    // ์ด๋ฉ”์ผ ํ˜•์‹ ํ™•์ธ
    if(android.util.Patterns.EMAIL_ADDRESS.matcher(emailValue).matches()) {
        // ๋น„๋ฐ€๋ฒˆํ˜ธ ํ˜•์‹ ํ™•์ธ
        if (Pattern.matches(
                "^(?=.*[A-Za-z])(?=.*[0-9])(?=.*[\$@\$!%*#?&])[A-Za-z[0-9]\$@\$!%*#?&]{8,20}\$", pwValue)) {
            // ๋‚˜๋จธ์ง€ ๊ฐ’ ๋นˆ ์นธ์ด ์žˆ๋Š”์ง€ ํ™•์ธ
            if(nameValue.isNotEmpty() && birthValue.isNotEmpty() && phoneValue.isNotEmpty() && addressValue.isNotEmpty()) {
                // ํŒŒ์ด์–ด๋ฒ ์ด์Šค Authentication ๊ณ„์ • ์ƒ์„ฑ
                createAccount(
                    binding.signupEditId.text.toString(),
                    binding.signupEditPw.text.toString()
                )
            } else {
                Toast.makeText(baseContext, "๋ชจ๋“  ํ•ญ๋ชฉ์„ ๋‹ค ์ž…๋ ฅํ•ด์ฃผ์„ธ์š”.", Toast.LENGTH_SHORT).show()

            }
        } else {
            Toast.makeText(baseContext, "8~16์ž ์˜๋ฌธ, ์ˆซ์ž, ํŠน์ˆ˜๋ฌธ์ž๋ฅผ ์‚ฌ์šฉํ•˜์„ธ์š”.", Toast.LENGTH_SHORT).show()
        }
    } else {
        Toast.makeText(baseContext, "์ด๋ฉ”์ผ ํ˜•์‹์ด ์•„๋‹™๋‹ˆ๋‹ค.", Toast.LENGTH_SHORT).show()
    }
}
  1. ์ด๋ฉ”์ผ ์ค‘๋ณต์—†์ด ํšŒ์›๊ฐ€์ž…์— ์„ฑ๊ณตํ•˜๋ฉด Firebase Authentication์— ๊ณ„์ •์„ ๋“ฑ๋กํ•œ๋‹ค. ์‹คํŒจ ์‹œ Toast ๋ฉ”์„ธ์ง€ ์ถœ๋ ฅ

แ„‰แ…ณแ„แ…ณแ„…แ…ตแ†ซแ„‰แ…ฃแ†บ 2022-12-09 แ„‹แ…ฉแ„Œแ…ฅแ†ซ 8 36 18

// ํšŒ์›๊ฐ€์ž… ํ•จ์ˆ˜
private fun createAccount(email: String, password: String) {
// ํŒŒ์ด์–ด๋ฒ ์ด์Šค ํšŒ์›๊ฐ€์ž… ๋ฉ”์„œ๋“œ
auth.createUserWithEmailAndPassword(email, password)
    .addOnCompleteListener(this) { task ->
        if (task.isSuccessful) {
            // ํšŒ์›๊ฐ€์ž… ์„ฑ๊ณต
            val user = Firebase.auth.currentUser
            user?.let {
                val email = user.email
                val uid = user.uid

                // ํŒŒ์ด์–ด๋ฒ ์ด์Šค Realtime Database ๋ฐ์ดํ„ฐ ์ €์žฅ
                writeNewUser(uid, nameValue, birthValue, phoneValue, addressValue)

                // ์„ค๋ฌธ์กฐ์‚ฌ ํŽ˜์ด์ง€๋กœ ์ด๋™
                var intent= Intent(this, SurveyActivity::class.java)
                intent.putExtra("user_id", email)
                startActivity(intent)
            }
        } else {
            // ํšŒ์›๊ฐ€์ž… ์‹คํŒจ ์‹œ ํ”„๋ ˆํผ๋Ÿฐ์Šค null ๊ฐ’์œผ๋กœ ์—…๋ฐ์ดํŠธ
            Toast.makeText(baseContext, "์ด๋ฏธ ์กด์žฌํ•˜๋Š” ์ด๋ฉ”์ผ์ž…๋‹ˆ๋‹ค.", Toast.LENGTH_SHORT).show()
        }
    }
 }
  1. ํšŒ์›๊ฐ€์ž…์—์„œ ์ž…๋ ฅํ•œ ์ •๋ณด๋ฅผ Firebase Realtime DataBase users์— ์ €์žฅํ•œ๋‹ค. ๊ฐ๊ฐ์˜ ํƒ€์ž…์€ ์•„๋ž˜์™€ ๊ฐ™๋‹ค.
ํƒ€์ž… ์„ค๋ฌธ ์ •๋ณด
String name, birth, phone, address

แ„‰แ…ณแ„แ…ณแ„…แ…ตแ†ซแ„‰แ…ฃแ†บ 2022-12-09 แ„‹แ…ฉแ„Œแ…ฅแ†ซ 7 51 59

// ํŒŒ์ด์–ด๋ฒ ์ด์Šค Realtime Database ์ €์žฅ ํ˜•์‹
@IgnoreExtraProperties
data class User(val name: String, val birth: String, val phone: String, val address: String) {
    // Null default values create a no-argument default constructor, which is needed
    // for deserialization from a DataSnapshot.
}

// Firebase Realtime Database ์ €์žฅ ํ•จ์ˆ˜
fun writeNewUser(email: String, name: String, birth: String, phone: String, address: String) {
    database = Firebase.database.reference
    val user = User(name, birth, phone, address)

    database.child("users").child(email).setValue(user)
}
  1. signInWithEmailAndPassword ๋ฉ”์„œ๋“œ๋ฅผ ์‚ฌ์šฉํ•ด Firebase Authentication์— ์ €์žฅ๋œ ๋ฐ์ดํ„ฐ์™€ ์ผ์น˜ ์—ฌ๋ถ€๋ฅผ ํŒ๋‹จํ•œ๋‹ค. Firebase Realtime Database์— ์ €์žฅ๋œ ๋ฐ์ดํ„ฐ๋ฅผ ์กฐํšŒํ•˜์—ฌ ๋ณ€์ˆ˜์— ์ €์žฅํ•œ ๋’ค ๋งˆ์ดํŽ˜์ด์ง€ ๋‚ด์šฉ์„ ์œ ์ €์— ๋งž๊ฒŒ ๋ณ€๊ฒฝํ•œ๋‹ค.
 // ๋กœ๊ทธ์ธ ํ•จ์ˆ˜
 private fun signIn(email: String, password: String) {
     auth.signInWithEmailAndPassword(email, password)
         .addOnCompleteListener(this) { task ->
             if (task.isSuccessful) {
                 // ๋กœ๊ทธ์ธ ์„ฑ๊ณตํ•˜๋ฉด ๋ฉ”์ธํ™”๋ฉด์œผ๋กœ ํ™”๋ฉด ์ „ํ™˜
                 intent = Intent(this,MainActivity::class.java)
                 intent.putExtra("user_id",binding.loginEditId.text.toString())
                 startActivity(intent)
                 val user = auth.currentUser

                 // ํŒŒ์ด์–ด๋ฒ ์ด์Šค Realtime Database ์กฐํšŒ ํ›„ ๊ทธ ์ •๋ณด๋กœ ํ”„๋ ˆํผ๋Ÿฐ์Šค ์—…๋ฐ์ดํŠธ
                 user?.let {
                     val uid = user.uid
                     val email = user.email

                     database = Firebase.database.reference
                     database.child("users").child(uid).child("name").get().addOnSuccessListener {
                         var nameValue = it.value.toString()
                     }
                     database.child("users").child(uid).child("birth").get().addOnSuccessListener {
                         var birthValue = it.value.toString()
                     }
                     database.child("users").child(uid).child("phone").get().addOnSuccessListener {
                         var phoneValue = it.value.toString()
                     }
                     database.child("users").child(uid).child("address").get().addOnSuccessListener {
                         var addressValue = it.value.toString()
                     }
                 }
             }
             else {
                 // ๋กœ๊ทธ์ธ ์‹คํŒจ ์‹œ ํ”„๋ ˆํผ๋Ÿฐ์Šค null ๊ฐ’์œผ๋กœ ์—…๋ฐ์ดํŠธ
                 Toast.makeText(baseContext, "์ด๋ฉ”์ผ ๋˜๋Š” ๋น„๋ฐ€๋ฒˆํ˜ธ๋ฅผ ์ž˜๋ชป ์ž…๋ ฅํ–ˆ์Šต๋‹ˆ๋‹ค.", Toast.LENGTH_SHORT).show()
             }
         }
 }
  1. ์ด๋ฏธ ๋กœ๊ทธ์ธํ•œ ์œ ์ €๋Š” ๋ณ„๋„์˜ ๋กœ๊ทธ์ธ์—†์ด ๋ฉ”์ธ ์•กํ‹ฐ๋น„ํ‹ฐ๋กœ ์ด๋™ํ•œ๋‹ค.
  override fun onStart() {
     super.onStart()
     // Check if user is signed in (non-null) and update UI accordingly.
     val currentUser = auth.currentUser
     if(currentUser != null){
         reload();
     }
 }

  // ๋กœ๊ทธ์ธ ๊ธฐ๋ก์ด ์žˆ์œผ๋ฉด ๋ฉ”์ธ ์•กํ‹ฐ๋น„ํ‹ฐ๋กœ ์ด๋™
  private fun reload() {
      var intent = Intent(this, MainActivity::class.java)
      startActivity(intent)
  }
  1. ๋น„๋ฐ€๋ฒˆํ˜ธ ์ˆ˜์ •์ด ์žˆ์œผ๋ฉด Firebase Authentication์— ๋น„๋ฐ€๋ฒˆํ˜ธ ์ˆ˜์ • ์š”์ฒญ์„ ๋ณด๋‚ด๊ณ  ๋‚˜๋จธ์ง€ ์ •๋ณด๋Š” Realtime Database์— ์ƒˆ ์ •๋ณด๋ฅผ ์ƒˆ๋กœ ์ €์žฅํ•œ๋‹ค.
// ์ˆ˜์ • ์™„๋ฃŒ ๋ฒ„ํŠผ
binding.profileEditTvFinish.setOnClickListener {
    val user = Firebase.auth.currentUser!!
    var newEmailValue = binding.profileEditEditId.text.toString()
    var newPwValue = binding.profileEditEditPw.text.toString()
    var newNameValue = binding.profileEditEditName.text.toString()
    var newBirthValue = binding.profileEditEditBirthday.text.toString()
    var newPhoneValue = binding.profileEditEditNumber.text.toString()
    var newAddressValue = binding.profileEditEditTown.text.toString()

    if (pwValue == newPwValue && nameValue == newNameValue && birthValue == newBirthValue && phoneValue == newPhoneValue && addressValue == newAddressValue) {
        Toast.makeText(baseContext, "์ˆ˜์ • ์‚ฌํ•ญ์ด ์—†์Šต๋‹ˆ๋‹ค.", Toast.LENGTH_SHORT).show()
    } else {
        if (Pattern.matches(
                "^(?=.*[A-Za-z])(?=.*[0-9])(?=.*[\$@\$!%*#?&])[A-Za-z[0-9]\$@\$!%*#?&]{8,20}\$", newPwValue)) {
            if(newNameValue.isNotEmpty() && newBirthValue.isNotEmpty() && newPhoneValue.isNotEmpty() && newAddressValue.isNotEmpty()) {
                // ํšŒ์› ์ •๋ณด ์ˆ˜์ •
                user?.let {
                    val uid = user.uid

                    // ์›๋ž˜ ๋น„๋ฐ€๋ฒˆํ˜ธ๋ž‘ ๋‹ค๋ฅด๋ฉด ๋น„๋ฐ€๋ฒˆํ˜ธ ์ˆ˜์ •
                    if(pwValue != newPwValue) {
                        // ํŒŒ์ด์–ด๋ฒ ์ด์Šค ์‚ฌ์šฉ์ž ์žฌ์ธ์ฆ - ์ด๋ฉ”์ผ, ๋น„๋ฐ€๋ฒˆํ˜ธ ์ˆ˜์ • ์ „ ํ•œ๋ฒˆ ํ•ด์ค˜์•ผํ•จ
                        val credential = EmailAuthProvider.getCredential(emailValue.toString(), pwValue.toString())
                        user.reauthenticate(credential).addOnCompleteListener {
                            Log.d("ํšŒ์›์ •๋ณด ์ˆ˜์ •", "์œ ์ € ์žฌ์ธ์ฆ ์™„๋ฃŒ")
                        }

                        // ํŒŒ์ด์–ด๋ฒ ์ด์Šค ํƒ‘์žฌ ๋น„๋ฐ€๋ฒˆํ˜ธ ์ˆ˜์ • ๋ฉ”์†Œ๋“œ
                        user!!.updatePassword(binding.profileEditEditPw.text.toString())
                            .addOnCompleteListener { task ->
                                if (task.isSuccessful) {
                                    Log.d("๋น„๋ฐ€๋ฒˆํ˜ธ ์ˆ˜์ •", "๋น„๋ฐ€๋ฒˆํ˜ธ ๋ณ€๊ฒฝ ์™„๋ฃŒ")
                                }
                            }
                    }

                    // ํŒŒ์ด์–ด๋ฒ ์ด์Šค ๋ฆฌ์–ผํƒ€์ž„ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ์ˆ˜์ •
                    fun updateUser(email: String, name: String, birth: String, phone: String, address: String) {
                        database = Firebase.database.reference
                        val user = SignupActivity.User(name, birth, phone, address)

                        database.child("users").child(email).setValue(user)

                        Toast.makeText(baseContext, "๊ฐœ์ธ ์ •๋ณด ์ˆ˜์ • ์™„๋ฃŒ๋˜์—ˆ์Šต๋‹ˆ๋‹ค.", Toast.LENGTH_SHORT).show()
                        finish()
                    }
                }
            } else {
                Toast.makeText(baseContext, "๋ชจ๋“  ํ•ญ๋ชฉ์„ ๋‹ค ์ž…๋ ฅํ•ด์ฃผ์„ธ์š”.", Toast.LENGTH_SHORT).show()

            }
        } else {
            Toast.makeText(baseContext, "8~16์ž ์˜๋ฌธ, ์ˆซ์ž, ํŠน์ˆ˜๋ฌธ์ž๋ฅผ ์‚ฌ์šฉํ•˜์„ธ์š”.", Toast.LENGTH_SHORT).show()
        }
    }
}
  1. ๋กœ๊ทธ์•„์›ƒ
// ๋กœ๊ทธ์•„์›ƒ ๊ธฐ๋Šฅ
binding.fgProfileTvLogout.setOnClickListener {
    Firebase.auth.signOut()
    var intent= Intent(requireContext(), LoginActivity::class.java)
    startActivity(intent)
}

2. ์„ค๋ฌธ์กฐ์‚ฌ ์ •๋ณด

  1. ์„ค๋ฌธ์กฐ์‚ฌ์—์„œ ์กฐ์‚ฌํ•œ ์„ค๋ฌธ์ •๋ณด๋ฅผ Firebase Realtime DataBase์— ์ €์žฅํ•œ๋‹ค. ๊ฐ๊ฐ์˜ ํƒ€์ž…์€ ์•„๋ž˜์™€ ๊ฐ™๋‹ค.
ํƒ€์ž… ์„ค๋ฌธ ์ •๋ณด
String user_id, user_height, user_weight, user_proteinPurpose, user_trainingPurpose, user_trainingTime, user_snackYn
Int user_proteinAmount
List? user_dietCnt, user_allergy
List? user_proPre, user_flaPre
   package com.example.firebasepratice

   class Survey {
     var user_id: String? = null
     var user_height: String? = null
     var user_weight: String? = null
     var user_proteinPurpose: String? = null
     var user_trainingPurpose: String? = null
     var user_trainingCnt: String? = null
     var user_trainingTime: String? = null
     var user_dietCnt: List<String>? = null
     var user_allergy: List<String>? = null
     var user_snackYn: String? = null
     var user_proPre: List<Int>? = null
     var user_flaPre: List<Int>? = null
     var user_proteinAmount: Int? = null



     internal constructor() {}
     constructor(
         user_id: String?,
         user_height: String?,
         user_weight: String?,
         user_proteinPurpose: String?,
         user_trainingPurpose: String?,
         user_trainingCnt: String?,
         user_trainingTime: String?,
         user_dietCnt: List<String>?,
         user_allergy: List<String>?,
         user_snackYn: String?,
         user_proPre: List<Int>?,
         user_flaPre: List<Int>?,
         user_proteinAmount: Int?


     ) {
         this.user_id = user_id
         this.user_height = user_height
         this.user_weight = user_weight
         this.user_proteinPurpose = user_proteinPurpose
         this.user_trainingPurpose = user_trainingPurpose
         this.user_trainingCnt = user_trainingCnt
         this.user_trainingTime = user_trainingTime
         this.user_dietCnt = user_dietCnt
         this.user_allergy = user_allergy
         this.user_snackYn = user_snackYn
         this.user_proPre = user_proPre
         this.user_flaPre = user_flaPre
         this.user_proteinAmount = user_proteinAmount
     }
 }

image

  1. ์บ˜๋ฆฐ๋”์—์„œ Firebase Realtime DataBase์— ์žˆ๋Š” ๋ฐ์ดํ„ฐ๋ฅผ ์ฝ์–ด์˜จ๋‹ค.
 firebaseDatabase = FirebaseDatabase.getInstance();
       databaseReference = firebaseDatabase!!.getReference().child("Survey")
       auth = Firebase.auth
       val email: String = (auth.currentUser?.email) as String
       databaseReference!!.get().addOnSuccessListener {
           val data = it.children.iterator().next().getValue() as HashMap<String, Any>
           Log.d("์ „๋ถ€๋‹ค", data.toString())
           var fflavour: Array<Int>? = null
           var fproduct: Array<Int>? = null
           var fproteinAmount: Int? = null
           if (data.get("user_id").toString() == email) {
               var sFFlavour = data.get("user_flaPre") as ArrayList<Int>?
               var sFProduct = data.get("user_proPre") as ArrayList<Int>?
               var sFProtein = data.get("user_proteinAmount") as Int
               fflavour = strToArray(sFFlavour!!)
               fproduct = strToArray(sFProduct!!).copyOf()
                fproteinAmount = sFProtein!!
            }
  1. ์„ค๋ฌธ์กฐ์‚ฌ๋ฅผ ํ•˜๋ฉด์„œ ์ €์žฅํ•ด๋‘” ์œ ์ €์˜ ํ‚ค, ๋ชธ๋ฌด๊ฒŒ, ํ•„์š”๋‹จ๋ฐฑ์งˆ๋Ÿ‰์„ ํ”„๋ž˜ํผ๋Ÿฐ์Šค๋กœ ์ €์žฅํ•˜์—ฌ ํ”„๋กœํ•„ ํ™”๋ฉด์—์„œ ์œ ์ €๊ฐ€ ํ™•์ธํ•  ์ˆ˜ ์žˆ๋„๋ก ํ•œ๋‹ค. ๊ทธ๋ฆฌ๊ณ  ์„ค๋ฌธ์กฐ์‚ฌ ์ˆ˜์ •ํ•˜๊ธฐ ๋ฒ„ํŠผ์„ ๋ˆ„๋ฅด๋ฉด ์„ค๋ฌธ์กฐ์‚ฌ ๋‚ด์šฉ์„ ์ˆ˜์ •ํ•  ์ˆ˜ ์žˆ๋‹ค.
if(weightValue == "User weight"){
          val sharedPreference = requireContext().getSharedPreferences("surveyInfo", Context.MODE_PRIVATE)
          val weightValue = sharedPreference.getString("weight", "User weight")
          val heightValue = sharedPreference.getString("height", "User height")
          val proteinAmountValue = sharedPreference.getString("proteinAmount", "User proteinAmount")
          binding.fgProfileTvWeight.setText("${weightValue}kg")
          binding.fgProfileTvHeight.setText("${heightValue}cm")
          binding.fgProfileTvProtein.setText("${proteinAmountValue}g")
      }

      // ์„ค๋ฌธ์กฐ์‚ฌ ์ˆ˜์ • ํŽ˜์ด์ง€๋กœ ์ด๋™
      binding.fgProfileLayoutSurvey.setOnClickListener{
          var intent = Intent(requireContext(),SurveyActivity::class.java)
          startActivity(intent)
      }

๐Ÿพ ๊ธฐ๋Šฅ ๊ตฌํ˜„

๐Ÿพ ๊ธฐ๋Šฅ ๊ตฌํ˜„

1. ํ•„์š” ๋‹จ๋ฐฑ์งˆ๋Ÿ‰ ์‚ฐ์ถœ ํ•จ์ˆ˜

์ œ์ง€๋ฐฉ : ์ „์ฒด ๋ชธ์—์„œ ์ง€๋ฐฉ๋Ÿ‰์„ ์ œ์™ธํ•œ ๋ถ€๋ถ„์˜ ๋ฌด๊ฒŒ๋ฅผ ๋ชจ๋‘ ํ•ฉํ•œ ๋ฌด๋ ˆ๋กœ ๊ทผ์œก, ๋ผˆ, ๊ธฐ๊ด€ ๋“ฑ์„ ํฌํ•จํ•œ ์ฒด์ค‘
์ œ์ง€๋ฐฉ ๊ณต์‹ : (1.10 * ์ฒด์ค‘kg ) - ( 128 * ( ์ฒด์ค‘kg์ œ๊ณฑ / ํ‚คcm์ œ๊ณฑ ) ) <ํ•„์š” ๋‹จ๋ฐฑ์งˆ๋Ÿ‰ ์‚ฐ์ถœ ๋ฐฉ๋ฒ•>

  1. ์ œ์ง€๋ฐฉ ๊ณต์‹์„ ํ†ตํ•ด์„œ ์ œ์ง€๋ฐฉ์„ ์‚ฐ์ถœํ•œ๋‹ค.
  2. ํ™œ๋™๊ณ„์ˆ˜์™€ ๋งž๊ฒŒ ์„ค๋ฌธ์กฐ์‚ฌ์—์„œ ์กฐ์‚ฌํ•œ ํŠธ๋ ˆ์ด๋‹ ๋ชฉ์  ๊ธฐ์ค€์œผ๋กœ ํ•„์š” ๋‹จ๋ฐฑ์งˆ๋Ÿ‰์„ ์‚ฐ์ถœํ•ด์ค€๋‹ค.
private fun calculateProtein(height: Int, weight: Int, purpose: String?) : Int{
        val leanFat: Int = ((1.10 * weight) - 128 * ((weight *  weight) / (height * height))).toInt()
        val result:Int = when (purpose) {
            "๋ณด๋””๋นŒ๋”ฉ ๋Œ€ํšŒ ์ค€๋น„" -> (leanFat * 2.0).toInt()
            "๋ฐ”๋”” ํ”„๋กœํ•„ ์ค€๋น„" -> (leanFat * 1.8).toInt()
            "๊ณจ๊ฒฉ๊ทผ๋Ÿ‰ ์ฆ๊ฐ€" -> (leanFat * 1.5).toInt() //165 84
            "์ฒด์ง€๋ฐฉ ๊ฐ๋Ÿ‰" -> (leanFat * 1.3).toInt()
            "๋ฒŒํฌ์—…" -> (leanFat * 1.75).toInt() //180 130
            "์›จ์ดํŠธ ํŠธ๋ ˆ์ด๋‹์„ ํ•˜์ง€ ์•Š์Œ" -> (leanFat * 1.1).toInt()
            else -> 0
        }
        return result
    }
  1. ๋งž์ถค์‹๋‹จ์„ ๋ณด์—ฌ์ฃผ๊ธฐ ์ „์— Alert์ฐฝ์œผ๋กœ ๋ฏธ๋ฆฌ ํ•„์š”๋‹จ๋ฐฑ์งˆ๋Ÿ‰์„ ๋ณด์—ฌ์ค€๋‹ค.
private fun onClickShowAlert(p: Int, flavour: Array<Int>, product: Array<Int>) {
       val myAlertBuilder: AlertDialog.Builder = AlertDialog.Builder(this@SurveyActivity)
       myAlertBuilder.setTitle("ํšŒ์›๋‹˜์˜ ํ•„์ˆ˜ ๋‹จ๋ฐฑ์งˆ๋Ÿ‰์€: ${p}์ž…๋‹ˆ๋‹ค")
       myAlertBuilder.setMessage("Ok๋ฒ„ํŠผ์„ ๋ˆ„๋ฅด๋ฉด ๋งž์ถค ์‹๋‹จ์„ ๋งŒ๋‚˜๋ณด์‹ค ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค! ์„ค๋ฌธ์„ ๋‹ค์‹œ ์ž‘์„ฑํ•˜๋ ค๋ฉด Cancel๋ฒ„ํŠผ์„ ๋ˆŒ๋Ÿฌ์ฃผ์„ธ์š”.")
       myAlertBuilder.setPositiveButton("Ok",
           DialogInterface.OnClickListener { dialog, which -> // OK ๋ฒ„ํŠผ์„ ๋ˆŒ๋ ธ์„ ๊ฒฝ์šฐ
               val intent = Intent(this, MainActivity::class.java)
               intent.putExtra("proteinAmount",p)
               intent.putExtra("flavour",flavour)
               intent.putExtra("product",product)
               startActivity(intent)
           })
       myAlertBuilder.setNegativeButton("Cancle",
           DialogInterface.OnClickListener { dialog, which -> // Cancle ๋ฒ„ํŠผ์„ ๋ˆŒ๋ ธ์„ ๊ฒฝ์šฐ
               Toast.makeText(
                   applicationContext, "Pressed Cancle",
                   Toast.LENGTH_SHORT
               ).show()
           })
       myAlertBuilder.show()
   }

2. ์œ ์ € ๋งž์ถคํ˜• ์‹๋‹จ ๊ตฌ์„ฑ ์•Œ๊ณ ๋ฆฌ์ฆ˜

  1. ์›”๋ณ„์— ๋งž๊ฒŒ ์ „์ฒด ์‹๋‹จ์˜ ์ˆ˜๋ฅผ ์กฐ์ •
  2. ์•Œ๋Ÿฌ์ง€๋ฅผ ์ œ์™ธํ•œ ์ƒํ’ˆ ๋ฆฌ์ŠคํŠธ ์ƒ์„ฑ
  3. ์ œํ’ˆ ์„ ํ˜ธ ๋น„์œจ์— ๋งž๊ฒŒ ํ•œ๋‹ฌ์น˜ ์ œํ’ˆ์„ ๋ฐฐ์ •ํ•˜๊ณ  ๋ชจ์ž๋ž€ ๋ถ€๋ถ„์€ ์ œ์ผ ์„ ํ˜ธ๊ฐ€ ๋†’์•˜๋˜ ์ œํ’ˆ์œผ๋กœ ์ฑ„์›Œ์ค€๋‹ค.
  4. ํ•˜๋ฃจ ํ•„์š” ๋‹จ๋ฐฑ์งˆ์–‘์„ ๊ณ ๋ คํ•˜์—ฌ ์‹๋‹จ ์‚ฐ์ถœํ•œ๋‹ค.
fun makeDietCalendar(proteinAmount: Int, flavour: Array<Int>, product: Array<Int>, allergy: Int, month: Int) : DietInfo {

        // 1. ๋ฐ›์•„์˜จ ๋‹ฌ์— ๋งž์ถฐ ํ•„์š”ํ•œ ์ด ์ƒํ’ˆ ๊ฐฏ์ˆ˜ ์‚ฐ์ถœ
        val case1 = arrayOf(1,3,4,7,8,10,12)
        val case2 = arrayOf(4,6,9,11)
        var totalAmount = 28 * 3
        if(case1.contains(month)) totalAmount = 31 * 3
        else if(case2.contains(month)) totalAmount = 30 * 3

        // 2. ์•Œ๋Ÿฌ์ง€๋ฅผ ์ œ์™ธํ•œ ์ƒํ’ˆ ๋ฆฌ์ŠคํŠธ ์ƒ์„ฑ
        val finalProduct = arrayListOf<Int>()
        var sum = 0         // ์ œํ’ˆ ์„ ํ˜ธ ๋น„์œจ์— ๋”ฐ๋ผ ์‹๋‹จ์„ ๊ตฌ์„ฑํ•˜๊ธฐ ์œ„ํ•œ ๋ณ€์ˆ˜
        var sumCheck = 0    // ๋น„์œจ๋ณ„๋กœ ๊ตฌ์„ฑํ•˜๊ณ  ๋ชจ์ž๋ž€ ์ œํ’ˆ์„ ์ฑ„์›Œ์ฃผ๊ธฐ ์œ„ํ•œ ๋ณ€์ˆ˜

        for(i : Int in 0 until product.size){
            if(i != allergy) {
                sum += product[i]
                finalProduct.add(product[i])
            }
            else if(i == allergy){
                finalProduct.add(0)
            }
        }
        Log.d("FINAL PRODUCT CHECK :: ", finalProduct.toString())

        // 3. ์ œํ’ˆ ์„ ํ˜ธ ๋น„์œจ์— ๋งž๊ฒŒ ํ•œ๋‹ฌ์น˜ ์ œํ’ˆ์„ ๋ฐฐ์ •ํ•˜๊ณ  ๋ชจ์ž๋ž€ ๋ถ€๋ถ„์€ ์ œ์ผ ์„ ํ˜ธ๊ฐ€ ๋†’์•˜๋˜ ์ œํ’ˆ์œผ๋กœ ์ฑ„์›Œ์คŒ
        var max = finalProduct[0]
        var idx = 0
        for(i : Int in 0 until finalProduct.size){
            if(finalProduct[i] > max) {
                max = finalProduct[i]
                idx = i
            }
            val value = ( totalAmount / sum * finalProduct[i]) / 1
            sumCheck += value
            finalProduct[i] = value // finalProduct ๋ฐฐ์—ด์„ ์„ ํ˜ธ ์ˆ˜์น˜์— ๋”ฐ๋ผ ํ•œ๋‹ฌ์น˜ ์ƒํ’ˆ ๊ฐฏ์ˆ˜ ๋น„์œจ์— ๋งž๊ฒŒ ์ˆ˜์ •
        }
	
        if(totalAmount - sumCheck > 0) finalProduct[idx] += (totalAmount - sumCheck) // ํ•œ๋‹ฌ์น˜ ์‹๋‹จ์„ ์งœ๊ณ  ๋ชจ์ž๋ž€ ๊ฐฏ์ˆ˜๋Š” ์ œ์ผ ์„ ํ˜ธ๋„๊ฐ€ ๋†’์€ ์ œํ’ˆ์œผ๋กœ ์ฑ„์›Œ์คŒ

        // 4. ํ•˜๋ฃจ ํ•„์š” ๋‹จ๋ฐฑ์งˆ์–‘์„ ๊ณ ๋ คํ•˜์—ฌ ์‹๋‹จ ์‚ฐ์ถœ
        // dietInfo์— 30์ผ์น˜ ์‹๋‹จ ๋„ฃ์–ด์„œ ๋ณด๋‚ด์ค„๊ฑฐ์ž„.
        var dietInfo = DietInfo()
        var localDb = LocalDB()
        var dietList = arrayListOf<String>()

        for(i : Int in 0 until finalProduct.size) {

            if (finalProduct[i] == 0) continue  // ์•Œ๋Ÿฌ์ง€ ์ƒํ’ˆ์€ ์ œ์™ธ
            val random = Random()

            for(j : Int in 0..finalProduct[i]){
                dietList.add(localDb.product[i][random.nextInt(localDb.product[i].size)].toString())
            }
        }
        dietList.shuffle() // ๊ตฌ์„ฑ๋œ ์ œํ’ˆ์„ ๋žœ์ ํ•˜๊ฒŒ ์…”ํ”Œ

        for(i : Int in 0 until (totalAmount/3)){
            dietInfo.calendar[i][0] = dietList.get(3*i)
            dietInfo.calendar[i][1] = dietList.get(3*i+1)
            dietInfo.calendar[i][2] = dietList.get(3*i+2)
        }

        // ๋‹จ๋ฐฑ์งˆ ์–‘์ด ๋ถ€์กฑํ•˜๋ฉด ์ฑ„์›Œ์คŒ
        if (70 < proteinAmount - 20){
            for(i : Int in 0 until (totalAmount/3)){
                dietInfo.calendar[i][3] = localDb.protein[i%10]
            }
            if (91 < proteinAmount - 20){
                for(i : Int in 0 until (totalAmount/3)){
                    dietInfo.calendar[i][4] = localDb.appetizer[i%5]
                }
            }
        }

        return dietInfo
    }


์ปดํ“จํ„ฐ ๊ตฌ์„ฑ / ํ•„์ˆ˜ ์กฐ๊ฑด ์•ˆ๋‚ด (Prerequisites) -->

  • ์•ˆ๋“œ๋กœ์ด๋“œ ์‚ฌํ–ฅ: 13 (API Level 32)
  • ์•ˆ๋“œ๋กœ์ด๋“œ ์ŠคํŠœ๋””์˜ค ์‚ฌํ–ฅ: Android Dolphin 2021.3.1
  • MinSdkVersion: 28
  • TargetSdkVrsion: 32
  • Kotlin Version: 1.7.20

๐Ÿ”จ๊ธฐ์ˆ  ์Šคํƒ (Technique Used)

kotlin Firebase

๐Ÿ“ฑํ”„๋กœ์ ํŠธ ์‚ฌ์šฉ๋ฒ• (Getting Started)

๐Ÿค๋ฐฑ๊ตฌ ์•ฑ ์‚ฌ์šฉ ๋ฐฉ๋ฒ•

1.	ํšŒ์›๊ฐ€์ž…์„ ํ•ด์ฃผ์„ธ์š”.
2.	ํšŒ์›๊ฐ€์ž…์„ ๋งˆ์นœ ์œ ์ €๋Š” ๋ฐ”๋กœ ์„ค๋ฌธ์กฐ์‚ฌ ํŽ˜์ด์ง€๋กœ ์ด๋™ํ•ฉ๋‹ˆ๋‹ค.
3.	์„ค๋ฌธ์กฐ์‚ฌ๋ฅผ ํ†ตํ•ด ์‹ ์ฒด ์ •๋ณด, ์šด๋™ ์ •๋ณด, ์„ ํ˜ธ ์ •๋ณด๋ฅผ ์ž…๋ ฅํ•ด์ฃผ์„ธ์š”.
4.	๋ฉ”์ธ ํŽ˜์ด์ง€๋ฅผ ํ†ตํ•ด ๋ฐฑ๊ตฌ ์„œ๋น„์Šค์—์„œ ์ œ๊ณตํ•˜๋Š” ์ œํ’ˆ๋“ค์„ ์—ด๋žŒํ•ด๋ณด์„ธ์š”! ์ƒํ•˜์ขŒ์šฐ ์Šคํฌ๋กค์„ ํ†ตํ•ด ๋ณด๋‹ค ๊ฐ„ํŽธํ•˜๊ฒŒ ์—ด๋žŒํ•˜์‹ค ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
5.	์ œํ’ˆ์˜ ์ƒ์„ธ ์ •๋ณด๋ฅผ ๋ณด๊ณ  ์‹ถ๋‹ค๋ฉด ํด๋ฆญํ•ด์ฃผ์„ธ์š”.
6.	์ƒํ’ˆ ์ƒ์„ธ ํŽ˜์ด์ง€์—์„œ๋Š” ์ œํ’ˆ ์†Œ๊ฐœ, ์›์žฌ๋ฃŒ ๋ฐ ํ•จ๋Ÿ‰, ์˜์–‘ ์„ฑ๋ถ„ ๋“ฑ ๋‹ค์–‘ํ•œ ์ •๋ณด๋ฅผ ์—ด๋žŒํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
7.	ํ•˜๋‹จ์˜ ๋„ค๋น„๊ฒŒ์ดํ„ฐ๋ฅผ ํ†ตํ•ด ์บ˜๋ฆฐ๋” ํŽ˜์ด์ง€๋กœ ์ด๋™ํ•˜์„ธ์š”.
8.	์บ˜๋ฆฐ๋” ํŽ˜์ด์ง€์—์„œ๋Š” ์˜ค๋Š˜์˜ ๋‚ ์งœ๋ฅผ ํ™•์ธํ•  ์ˆ˜ ์žˆ๊ณ  ๋”๋ณด๊ธฐ ๋ฒ„ํŠผ์„ ๋ˆŒ๋Ÿฌ ์ƒ์„ธํ•œ ์‹๋‹จ์„ ํ™•์ธํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
9.	์ƒ์„ธ ์‹๋‹จ ํŽ˜์ด์ง€์—์„œ๋Š” ๊ตฌ์„ฑ๋œ ์‹๋‹จ์„ ํ™•์ธํ•  ์ˆ˜ ์žˆ๊ณ , ์ขŒ์šฐ ์Šคํฌ๋กค์„ ํ†ตํ•ด ๋‹ค๋ฅธ ๋‚ ์งœ์˜ ์‹๋‹จ๋„ ํ™•์ธ ๊ฐ€๋Šฅํ•ฉ๋‹ˆ๋‹ค.
10.	๋˜, ์ƒ์„ธ ์‹๋‹จ ํŽ˜์ด์ง€์—์„œ๋Š” ์˜ค๋Š˜ ์„ญ์ทจํ•œ ์ œํ’ˆ์„ ์ฒดํฌํ•˜์—ฌ ๊ธฐ๋กํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
11.	์ฒดํฌ ํ›„ ์‹๋‹จ ํŽ˜์ด์ง€๋กœ ๋˜๋Œ์•„์˜ค๋ฉด ํ•˜๋ฃจ ๋ชฉํ‘œ ์„ญ์ทจ ๋‹จ๋ฐฑ์งˆ๋Ÿ‰๊ณผ ๋‚ด๊ฐ€ ์ง€๊ธˆ๊นŒ์ง€ ์„ญ์ทจํ•œ ๋‹จ๋ฐฑ์งˆ๋Ÿ‰์„ ํ•œ๋ˆˆ์— ๋ณผ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
12.	ํ•˜๋‹จ์˜ ๋„ค๋น„๊ฒŒ์ดํ„ฐ๋ฅผ ํ†ตํ•ด ๋งˆ์ดํŽ˜์ด์ง€๋กœ ์ด๋™ํ•˜์„ธ์š”.
13.	๋งˆ์ดํŽ˜์ด์ง€์—๋Š” ์œ ์ €์˜ ๊ธฐ๋ณธ์ ์ธ ์ •๋ณด๊ฐ€ ๋‚˜ํƒ€๋‚˜๊ฒŒ ๋ฉ๋‹ˆ๋‹ค.
14.	๋งˆ์ดํŽ˜์ด์ง€์˜ ํšŒ์›์ •๋ณด ์ˆ˜์ •, ์„ค๋ฌธ์ •๋ณด ์ˆ˜์ • ๋ฒ„ํŠผ์„ ํด๋ฆญํ•ด ์•ž์„œ ๊ธฐ์ž…ํ•œ ์ •๋ณด๋ฅผ ์ˆ˜์ •ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
15.	๋กœ๊ทธ์•„์›ƒ ๋ฒ„ํŠผ์„ ๋ˆŒ๋Ÿฌ ์„œ๋ฒ„์™€์˜ ์„ธ์…˜์„ ์ •์ƒ์ ์œผ๋กœ ์ข…๋ฃŒํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
16.	์•ž์„œ ๊ฐ€์ž…ํ•œ ํšŒ์› ์ •๋ณด๋ฅผ ํ†ตํ•ด ๋กœ๊ทธ์ธํ•˜์—ฌ ๊ณ„์†ํ•ด์„œ ์„œ๋น„์Šค ์ด์šฉ์ด ๊ฐ€๋Šฅํ•ฉ๋‹ˆ๋‹ค.

แ„‰แ…ณแ„แ…ณแ„…แ…ตแ†ซแ„‰แ…ฃแ†บ 2022-12-09 แ„‹แ…ฉแ„’แ…ฎ 7 08 43 ์‹œ์—ฐ ์˜์ƒ


๐Ÿ“ˆํ”„๋กœ์ ํŠธ ์ „๋ง

๐ŸŽ๊ฐœ์„ ํ•  ์ 

ย  ๋กœ๊ทธ์ธ ๊ธฐ๋Šฅ :

  • ํŒŒ์ด์–ด๋ฒ ์ด์Šค ํ†ตํ•ด ํšŒ์› ๊ด€๋ฆฌ๋ฅผ ํ•˜๊ณ  ์žˆ๋Š”๋ฐ ์ถ”๊ฐ€๋กœ ์†Œ์…œ ๋กœ๊ทธ์ธ ๊ธฐ๋Šฅ์„ ๊ตฌํ˜„ํ•˜๊ธฐ

ย  ํšŒ์› ๊ฐ€์ž… ๊ธฐ๋Šฅ:

  • string์œผ๋กœ ๋ฐ›๋Š” ์ฃผ์†Œ ๋ถ€๋ถ„์„ ์ฃผ์†Œ API๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ๊ฐœ์„ ํ•˜๊ธฐ
  • ์ „ํ™”๋ฒˆํ˜ธ, ์ƒ๋…„์›”์ผ string ๋ฐ›๋Š” ๊ฒƒ์ด ์•„๋‹Œ ์ •ํ™•ํ•œ ํฌ๋งท์œผ๋กœ ์ •๋ณด ์ˆ˜์ง‘ํ•˜๊ธฐ

ย  ์ƒํ’ˆ๋ชฉ๋ก :

  • ์ƒํ’ˆ๋ชฉ๋ก ์นดํ…Œ๊ณ ๋ฆฌ์™€ ์ƒํ’ˆ์„ ๋‹ค์–‘ํ™”ํ•˜๊ธฐ
  • ์นดํ…Œ๊ณ ๋ฆฌ ๋ฟ๋งŒ ์•„๋‹ˆ๋ผ ๋ฉ”์ธ ํŽ˜์ด์ง€์— ์ถ”์ฒœ ์ œํ’ˆ ๋‚˜ํƒ€๋‚ด๊ธฐ

ย  ์„ค๋ฌธ์กฐ์‚ฌ :

  • ์•Œ๋ ˆ๋ฅด๊ธฐ ํ•ญ๋ชฉ ์„ธ๋ถ„ํ™”ํ•˜์—ฌ ์ ์šฉ
  • ์„ค๋ฌธ์กฐ์‚ฌ UI ๋” ๋ณด๊ธฐ ์ข‹๊ฒŒ ๊ฐœ์„ ํ•˜๊ธฐ

ย  ์‹๋‹จ๊ตฌ์„ฑ :

  • ํ•„์š” ๋‹จ๋ฐฑ์งˆ ์„ญ์ทจ๋Ÿ‰์— ๋”์šฑ Fitํ•œ ์‹๋‹จ ๊ตฌ์„ฑ ์•Œ๊ณ ๋ฆฌ์ฆ˜ ๊ตฌํ˜„
  • ๋ˆ„์ ๋œ ์œ ์ € ๋ฐ์ดํ„ฐ๋ฅผ ๋ฐ”ํƒ•์œผ๋กœ ์‹๋‹จ ์ถ”์ฒœ AI ๋ชจ๋ธ ๊ฐœ๋ฐœ

ย  ๋งˆ์ดํŽ˜์ด์ง€ :

  • ๋‹จ๋ฐฑ์งˆ ์„ญ์ทจ๋Ÿ‰ ์กฐ์ ˆํ•  ์ˆ˜ ์žˆ๊ฒŒ ์„ค์ •ํ•˜๊ธฐ
  • ์„ค๋ฌธ์กฐ์‚ฌ ์ˆ˜์ • ๋ถ€๋ถ„ ์ˆ˜์ • ์ฒดํฌ ํ•ญ๋ชฉ ๊ฐ„ํŽธํ•˜๊ฒŒ ๊ฐœ์„ ํ•˜๊ธฐ

๐Ÿ’ก๋ฐœ์ „ ๊ฐ€๋Šฅ ๋ฐฉํ–ฅ

  • 2030 ๋Œ€์ƒ์œผ๋กœ ์‚ฌ์šฉ์ž ๋ถ„์„์„ ์‹ค์‹œ ํ•œ ๊ฒฐ๊ณผ ๋‹จ๋ฐฑ์งˆ ์‹๋‹จ ํŽธ์„ฑ ์„œ๋น„์Šค์— ๊ธ์ •์ ์ธ
    ๋ฐ˜์‘์„ ๋ณด์˜€๊ณ  ํŽธ์„ฑ ์‹๋‹จ์— ๋งž์ถ˜ ๋ฐฐ๋‹ฌ ์„œ๋น„์Šค์— ๋Œ€ํ•˜์—ฌ ๊ด€์‹ฌ์„ ๋ณด์˜€๋‹ค.
  • ๋ถ„์„ ๊ฒฐ๊ณผ์— ๋”ฐ๋ผ ๊ฒฐ์ œ ๋ฐ ๋ฐฐ์†ก ์„œ๋น„์Šค์— ๋Œ€ํ•œ ๊ธฐ๋Šฅ๋“ค์„ ์ถ”๊ฐ€์ ์œผ๋กœ ๊ตฌํ˜„ํ•˜๊ณ  ๊ฐœ์„ ํ•ด
    ์‹๋‹จ ์ถ”์ฒœ ์„œ๋น„์Šค๋ฅผ ๋ฐ›๊ณ  ์ง์ ‘ ์ƒํ’ˆ๊นŒ์ง€ ๊ตฌ๋งคํ•  ์ˆ˜ ์žˆ๋Š” ํ”Œ๋žซํผ ์„œ๋น„์Šค๋กœ ๋งŽ์€ ์‚ฌ์šฉ์ž์˜
    ๊ด€์‹ฌ์„ ๋ฐ›์„ ์ˆ˜ ์žˆ๋Š” ์•ฑ์œผ๋กœ ๋ฐœ์ „์‹œํ‚ฌ ์ˆ˜ ์žˆ์„ ๊ฒƒ์ด๋ผ ์˜ˆ์ƒํ•œ๋‹ค.

๐Ÿ•‹ํŒ€ ์ •๋ณด (Team Information)

์•ˆ๋…•ํ•˜์‹ญ๋‹ˆ๊นŒ, Team BaekGu์ž…๋‹ˆ๋‹ค.

Name Role github e-mail ์ƒ์„ธ ๊ธฐ๋Šฅ
Junwon Seo Leader / Back - End(Firebase) Realtime Firebase๋ฅผ ํ™œ์šฉํ•œ ๋ฐ์ดํ„ฐ ํ™œ์šฉ ๋ฐ ์ƒํ’ˆ ๊ฒ€์ƒ‰ ๊ธฐ๋Šฅ ๊ตฌํ˜„
์„ค๋ฌธ์กฐ์‚ฌ ๋ฐ ํ•„์š” ๋‹จ๋ฐฑ์งˆ๋Ÿ‰ ์ •๋ณด๋ฅผ ๊ธฐ๋ฐ˜์œผ๋กœ ์œ ์ €๋ณ„ ๋งž์ถค ์‹๋‹จ ๊ตฌ์„ฑ API ๊ตฌํ˜„
๋ฏผ์ค‘๋‹˜ Front-End (kotlin) ํšŒ์›๊ฐ€์ž… / ๋กœ๊ทธ์ธ, ์ƒํ’ˆ / ์บ˜๋ฆฐ๋” / ํ”„๋กœํ•„, ์‹๋‹จ ์ƒ์„ธ ํŽ˜์ด์ง€ ๊ตฌํ˜„
๋ฐ˜์‘ํ˜• ๋ ˆ์ด์•„์›ƒ , ๋””์ž์ธ์ ์šฉ
Huijae Ryu Back-End (Firebase) Realtime Firebase๋ฅผ ํ™œ์šฉํ•œ ์„ค๋ฌธ์กฐ์‚ฌ ์ €์žฅ ๋ฐ ๊ด€๋ฆฌ ๊ธฐ๋Šฅ ๊ตฌํ˜„
์„ค๋ฌธ์กฐ์‚ฌ ์ •๋ณด๋ฅผ ๊ธฐ๋ฐ˜์œผ๋กœ ํ•„์š”๋‹จ๋ฐฑ์งˆ๋Ÿ‰ ์‚ฐ์ถœ๊ธฐ๋Šฅ ๊ตฌํ˜„
Sungeun Yeo Front-End (kotlin) ๋กœ๊ณ  ๋””์ž์ธ ๋ฐ ์Šคํ”Œ๋ž˜์‹œ ํ™”๋ฉด ๊ตฌํ˜„
์„ค๋ฌธ์กฐ์‚ฌ, ์ƒํ’ˆ ๋ชฉ๋ก, ์ƒํ’ˆ ์ƒ์„ธ ์ •๋ณด ํŽ˜์ด์ง€ ๊ตฌํ˜„
jisu Seo Back-End (Firebase) ํšŒ์›๊ฐ€์ž…, ๋กœ๊ทธ์ธ, ์ž๋™ ๋กœ๊ทธ์ธ, ํšŒ์› ์ •๋ณด ์ˆ˜์ • ๊ธฐ๋Šฅ ๊ตฌํ˜„ ๋ฉ”์ธ ํŽ˜์ด์ง€, ์ƒํ’ˆ ์ƒ์„ธ ํŽ˜์ด์ง€ ์ƒํ’ˆ ์กฐํšŒ ๊ธฐ๋Šฅ ๊ตฌํ˜„

๊ฐœ๋ฐœ ๋ฐ ํ˜‘์—… ํ”Œ๋žซํผ

Google Drive Github Zoom Notion

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Contributors 5