๐์ฃผ์ ์ ์ ๋ฌธ์
- Instagram์ #์ค์ด์(์ค๋์ด๋์๋ฃ)์ธ์ฆ๊ธ์ 385๋ง๊ฐ ํฌ์ค๋ฅผ ์ฃผ์ ๋ก ํ๋ ์ ํ๋ฒ๋ค์ ๊ตฌ๋ ์๊ฐ 100๋ง๋ช ์ด ๋๋ ๋ฑ SNS์ ํ๋ซํผ์ผ๋ก MZ์ธ๋๊ฐ ๋ง์ ์๊ทน์ ๋ฐ์
- '์๊ธฐ๊ด๋ฆฌ = ๊ฑด๊ฐ๊ด๋ฆฌ' ๋ผ๋ ์ฌํ์ ๋ถ์๊ธฐ๊ฐ ํ์ฑ๋๋ฉด์ "๋ค๋ฒจ ์ด์ฝ๋ ธ๋ฏธ" ์๋ ์ฆ, ๊ฑด๊ฐ๊ณผ ์ด๋, ์ฒด๋ ฅ ๊ด๋ฆฌ์ ๋ํ ๊ด์ฌ์ด ์ปค์ง๋ฉด์ ๊ฑด๊ฐ ๊ด๋ จ ์์ฅ์ด ๊ธ์ฑ์ฅํ๋ ๊ฒฝ์ ํ์์ด๋ ์๋น ํจํด์ด ์ด๋ค์ง๊ณ ์๋ค.
![]() |
![]() |
![]() |
| "๋ํ๋ด์ผ ํต๊ณ์๋ฃ" | ์ธ์คํ #์ค์ด์ ์ธ์ฆ๊ธ | ํฌ์ค ์ ํ๋ฒ "ํผ์ง์ปฌ ๊ฐค๋ฌ๋ฆฌ" |
- ํฌ์ค์ ์ธ๊ธฐ๊ฐ ๋์์ง๋ฉด์ ๋จ๋ฐฑ์ง ์๋จ๋ ํฌ๊ฒ ๊ฐ๊ด์ ๋ฐ๊ณ ์๋ค.
- ๊ทธ๋ฌ๋ ์๋จ๊ด๋ฆฌ ์ฑ์ด ๋์ณ๋๋ ์ ๋ฐ์ ์ธ ์ด์ ์ ์ฒด์ค๊ฐ๋์ด์๊ณ , ํฌ์๋ค์๊ฒ ํ์ํ ๋จ๋ฐฑ์ง ๋ง์ถค ์๋จ ์ถ์ฒ์ฑ์ ๋ถ์ฌํ๋ค๋ ๊ฒ์ ์ธ์งํ์๋ค.
๊ทธ๋์ ์ฐ๋ฆฌ๋ ์๋์ ๊ธฐ๋ฅ๋ค์ ๊ตฌํํ์ฌ ๋จ๋ฐฑ์ง ์๋จ ๊ด๋ฆฌ ํธ์ฑ์ ๋ํ ๋ฒ๊ฑฐ๋ก์๊ณผ ๋จ์ผํ ๋จ๋ฐฑ์ง ์ํ์ ๋ํ ๊ถํ๊ฐ์ ํด์์ํค๋
ํฌ์& ๋ค์ด์ดํฐ๋ฅผ ์ํ๊ฐ์ธ ๋ง์ถคํ ๋จ๋ฐฑ์ง ์๋จ ๊ด๋ฆฌ ๋ฐ ์ ๊ณต ์๋น์ค๋ฅผ ์ ๊ณตํ๊ธฐ๋ก ํ๋ค.
- ์ค๋ฌธ ์กฐ์ฌ๋ฅผ ํตํด ๊ฐ์ธ์ ๋ณด์ ์ํ์ ๋ํ ์ ํธ๋์กฐ์ฌ๋ฅผ ๋ฐํ์ผ๋ก ๋จ๋ฐฑ์ง ์๋จ์ ์ถ์ฒ, ์ ๊ณต
- ํ๋ฃจ ๋ชฉํ ๋จ๋ฐฑ์ง ์ญ์ทจ์จ์ ์ฒดํฌํด ์ฃผ๋ ๊ธฐ๋ฅ ์ ๊ณต
- ์นดํ ๊ณ ๋ฆฌ ๋ณ ๊ฐ๋ณ ์ํ ํ์ธ ๊ธฐ๋ฅ ์ ๊ณต
๐จ์ผ๋ฌ์คํธ
๋จ๋ฐฑ์ง ๊ตฌ๋ ์ ์ค์๋ง๋ก "๋ฐฑ๊ตฌ"๋ผ๋ ์ด๋ฏธ์ง๋ฅผ ๋ ์ฌ๋ ธ๊ณ , ์ผ๋ฌ์คํธ๋ก ๊ตฌํํด์ splashํ๋ฉด, ๋ก๊ทธ์ธ ํ๋ฉด, ์ฑ ์ด๋ฏธ์ง์ ํ์ฉํ์๋ค.![]() |
![]() |
![]() |
๐๊ฐ๋ฐ ๋ฌธ์
- ๋ณธ ์ฝ๋๋ 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๊ด๋ จ ํด๋์ค๋ค์ด ์์นํด ์๋ค.
์ฌ์ฉ๋ ๋ผ์ด๋ธ๋ฌ๋ฆฌ
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์ ์ ์ฅํ๊ณ ์ค๋ฌธ ์กฐ์ฌ ํญ๋ชฉ๋ค์ ๋ฐํ์ผ๋ก ๋ชฉํ ๋จ๋ฐฑ์ง ์ญ์ทจ๋์ ๊ณ์ฐํด ์ค๋๋ค.
[๋ฉ์ธ ํ์ด์ง (ํผ๋)]
![]() |
![]() |
์ํ ์คํฌ๋กค์ ํตํด ์นดํ ๊ณ ๋ฆฌ ์ด๋์ด ๊ฐ๋ฅํ๊ณ ์นดํ ๊ณ ๋ฆฌ์์ ์ข์ฐ ์คํฌ๋กค ์ ํด๋น ์นดํ ๊ณ ๋ฆฌ์ ์ ํ์ ํ์ธํ ์ ์์ต๋๋ค.
![]() |
![]() |
๋ฉ์ธํ๋ฉด ์ ๊ฐ๋ณ ์ ํ์ ํด๋ฆญํ๋ฉด ํด๋น ์ ํ์ ์์ ์ฑ๋ถ๊ณผ ์ํ์ ๋ณด๋ฅผ ํ์ธํ ์ ์์ต๋๋ค.
[๋ฉ์ธ ํ์ด์ง (์บ๋ฆฐ๋)]
![]() |
![]() |
![]() |
๋ฉ์ธํ๋ฉด์ ์บ๋ฆฐ๋ ๋ถ๋ถ์์๋ ์ค๋ฌธ์กฐ์ฌ์ ๋ฐ๋ผ ํธ์ฑ๋ ํ ๋ฌ ์น ์๋จ์ ํ์ธํ ์ ์์ต๋๋ค.
๋ ์ง๋ฅผ ํด๋ฆญํ๊ณ ๋ ๋ณด๊ธฐ๋ฅผ ๋๋ฅด๋ฉด ์์ธ ์๋จ์ ํ์ธํ ์ ์๊ณ ์ข์ฐ ์คํฌ๋กค์ ํ๋ฉด ๊ทผ์ ๋ ์ง์ ์๋จ๋ ํ์ธํ ์์์ต๋๋ค.
์ญ์ทจํ ๋ชฉ๋ก์ ์ฒดํฌ๋ฉด ๊ทธ ๋ ์ง์ ์ญ์ทจํ ๋จ๋ฐฑ์ง๋์ ์
๋ฐ์ดํธํด์ฃผ์ด ํ์ธํ ์ ์์ต๋๋ค.
[๋ฉ์ธ ํ์ด์ง (๋ง์ดํ์ด์ง)]
![]() |
![]() |
![]() |
๊ฐ์ธ์ ๋ณด ์์ ๋ฒํผ์ ํด๋ฆญ ์ ํ๋กํ ์์ ํ๋ฉด์ด ๋ํ๋๊ณ ๊ฐ์ธ์ ๋ณด๋ฅผ ๋ณ๊ฒฝํ ์ ์์ต๋๋ค.
๋ํ ์ค๋ฌธ์กฐ์ฌ ์ ๋ณด ์์ ์ ํด๋ฆญ ์ ์ค๋ฌธ์กฐ์ฌ๋ฅผ ๋ค์์งํํ์ฌ ์ค๋ฌธ ์ ๋ณด๋ฅผ ๋ณ๊ฒฝํ ์ ์์ต๋๋ค.
- ์ด๋ฉ์ผ ํ์๊ณผ ๋น๋ฐ๋ฒํธ ํ์(์๋ฌธ, ์ซ์, ํน์๋ฌธ์ ์กฐํฉ), ๋น์นธ ์ ๋ฌด๋ฅผ ํ์ธ ํ ์ด์์ด ์์ผ๋ฉด ํ์๊ฐ์ ํจ์๋ฅผ ์คํํ๋ค.
// ํ์๊ฐ์
์๋ฃ ๋ฒํผ
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()
}
}- ์ด๋ฉ์ผ ์ค๋ณต์์ด ํ์๊ฐ์ ์ ์ฑ๊ณตํ๋ฉด Firebase Authentication์ ๊ณ์ ์ ๋ฑ๋กํ๋ค. ์คํจ ์ Toast ๋ฉ์ธ์ง ์ถ๋ ฅ
// ํ์๊ฐ์
ํจ์
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()
}
}
}- ํ์๊ฐ์ ์์ ์ ๋ ฅํ ์ ๋ณด๋ฅผ Firebase Realtime DataBase users์ ์ ์ฅํ๋ค. ๊ฐ๊ฐ์ ํ์ ์ ์๋์ ๊ฐ๋ค.
| ํ์ | ์ค๋ฌธ ์ ๋ณด |
| String | name, birth, phone, address |
// ํ์ด์ด๋ฒ ์ด์ค 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)
}- 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()
}
}
}- ์ด๋ฏธ ๋ก๊ทธ์ธํ ์ ์ ๋ ๋ณ๋์ ๋ก๊ทธ์ธ์์ด ๋ฉ์ธ ์กํฐ๋นํฐ๋ก ์ด๋ํ๋ค.
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)
}
- ๋น๋ฐ๋ฒํธ ์์ ์ด ์์ผ๋ฉด 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()
}
}
}- ๋ก๊ทธ์์
// ๋ก๊ทธ์์ ๊ธฐ๋ฅ
binding.fgProfileTvLogout.setOnClickListener {
Firebase.auth.signOut()
var intent= Intent(requireContext(), LoginActivity::class.java)
startActivity(intent)
}- ์ค๋ฌธ์กฐ์ฌ์์ ์กฐ์ฌํ ์ค๋ฌธ์ ๋ณด๋ฅผ 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
}
}- ์บ๋ฆฐ๋์์ 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!!
}- ์ค๋ฌธ์กฐ์ฌ๋ฅผ ํ๋ฉด์ ์ ์ฅํด๋ ์ ์ ์ ํค, ๋ชธ๋ฌด๊ฒ, ํ์๋จ๋ฐฑ์ง๋์ ํ๋ํผ๋ฐ์ค๋ก ์ ์ฅํ์ฌ ํ๋กํ ํ๋ฉด์์ ์ ์ ๊ฐ ํ์ธํ ์ ์๋๋ก ํ๋ค. ๊ทธ๋ฆฌ๊ณ ์ค๋ฌธ์กฐ์ฌ ์์ ํ๊ธฐ ๋ฒํผ์ ๋๋ฅด๋ฉด ์ค๋ฌธ์กฐ์ฌ ๋ด์ฉ์ ์์ ํ ์ ์๋ค.
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.10 * ์ฒด์คkg ) - ( 128 * ( ์ฒด์คkg์ ๊ณฑ / ํคcm์ ๊ณฑ ) )
<ํ์ ๋จ๋ฐฑ์ง๋ ์ฐ์ถ ๋ฐฉ๋ฒ>
- ์ ์ง๋ฐฉ ๊ณต์์ ํตํด์ ์ ์ง๋ฐฉ์ ์ฐ์ถํ๋ค.
- ํ๋๊ณ์์ ๋ง๊ฒ ์ค๋ฌธ์กฐ์ฌ์์ ์กฐ์ฌํ ํธ๋ ์ด๋ ๋ชฉ์ ๊ธฐ์ค์ผ๋ก ํ์ ๋จ๋ฐฑ์ง๋์ ์ฐ์ถํด์ค๋ค.
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
}- ๋ง์ถค์๋จ์ ๋ณด์ฌ์ฃผ๊ธฐ ์ ์ 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()
}- ์๋ณ์ ๋ง๊ฒ ์ ์ฒด ์๋จ์ ์๋ฅผ ์กฐ์
- ์๋ฌ์ง๋ฅผ ์ ์ธํ ์ํ ๋ฆฌ์คํธ ์์ฑ
- ์ ํ ์ ํธ ๋น์จ์ ๋ง๊ฒ ํ๋ฌ์น ์ ํ์ ๋ฐฐ์ ํ๊ณ ๋ชจ์๋ ๋ถ๋ถ์ ์ ์ผ ์ ํธ๊ฐ ๋์๋ ์ ํ์ผ๋ก ์ฑ์์ค๋ค.
- ํ๋ฃจ ํ์ ๋จ๋ฐฑ์ง์์ ๊ณ ๋ คํ์ฌ ์๋จ ์ฐ์ถํ๋ค.
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
}
- ์๋๋ก์ด๋ ์ฌํฅ: 13 (API Level 32)
- ์๋๋ก์ด๋ ์คํ๋์ค ์ฌํฅ: Android Dolphin 2021.3.1
- MinSdkVersion: 28
- TargetSdkVrsion: 32
- Kotlin Version: 1.7.20
![]() |
![]() |
| kotlin | Firebase |
1. ํ์๊ฐ์
์ ํด์ฃผ์ธ์.
2. ํ์๊ฐ์
์ ๋ง์น ์ ์ ๋ ๋ฐ๋ก ์ค๋ฌธ์กฐ์ฌ ํ์ด์ง๋ก ์ด๋ํฉ๋๋ค.
3. ์ค๋ฌธ์กฐ์ฌ๋ฅผ ํตํด ์ ์ฒด ์ ๋ณด, ์ด๋ ์ ๋ณด, ์ ํธ ์ ๋ณด๋ฅผ ์
๋ ฅํด์ฃผ์ธ์.
4. ๋ฉ์ธ ํ์ด์ง๋ฅผ ํตํด ๋ฐฑ๊ตฌ ์๋น์ค์์ ์ ๊ณตํ๋ ์ ํ๋ค์ ์ด๋ํด๋ณด์ธ์! ์ํ์ข์ฐ ์คํฌ๋กค์ ํตํด ๋ณด๋ค ๊ฐํธํ๊ฒ ์ด๋ํ์ค ์ ์์ต๋๋ค.
5. ์ ํ์ ์์ธ ์ ๋ณด๋ฅผ ๋ณด๊ณ ์ถ๋ค๋ฉด ํด๋ฆญํด์ฃผ์ธ์.
6. ์ํ ์์ธ ํ์ด์ง์์๋ ์ ํ ์๊ฐ, ์์ฌ๋ฃ ๋ฐ ํจ๋, ์์ ์ฑ๋ถ ๋ฑ ๋ค์ํ ์ ๋ณด๋ฅผ ์ด๋ํ ์ ์์ต๋๋ค.
7. ํ๋จ์ ๋ค๋น๊ฒ์ดํฐ๋ฅผ ํตํด ์บ๋ฆฐ๋ ํ์ด์ง๋ก ์ด๋ํ์ธ์.
8. ์บ๋ฆฐ๋ ํ์ด์ง์์๋ ์ค๋์ ๋ ์ง๋ฅผ ํ์ธํ ์ ์๊ณ ๋๋ณด๊ธฐ ๋ฒํผ์ ๋๋ฌ ์์ธํ ์๋จ์ ํ์ธํ ์ ์์ต๋๋ค.
9. ์์ธ ์๋จ ํ์ด์ง์์๋ ๊ตฌ์ฑ๋ ์๋จ์ ํ์ธํ ์ ์๊ณ , ์ข์ฐ ์คํฌ๋กค์ ํตํด ๋ค๋ฅธ ๋ ์ง์ ์๋จ๋ ํ์ธ ๊ฐ๋ฅํฉ๋๋ค.
10. ๋, ์์ธ ์๋จ ํ์ด์ง์์๋ ์ค๋ ์ญ์ทจํ ์ ํ์ ์ฒดํฌํ์ฌ ๊ธฐ๋กํ ์ ์์ต๋๋ค.
11. ์ฒดํฌ ํ ์๋จ ํ์ด์ง๋ก ๋๋์์ค๋ฉด ํ๋ฃจ ๋ชฉํ ์ญ์ทจ ๋จ๋ฐฑ์ง๋๊ณผ ๋ด๊ฐ ์ง๊ธ๊น์ง ์ญ์ทจํ ๋จ๋ฐฑ์ง๋์ ํ๋์ ๋ณผ ์ ์์ต๋๋ค.
12. ํ๋จ์ ๋ค๋น๊ฒ์ดํฐ๋ฅผ ํตํด ๋ง์ดํ์ด์ง๋ก ์ด๋ํ์ธ์.
13. ๋ง์ดํ์ด์ง์๋ ์ ์ ์ ๊ธฐ๋ณธ์ ์ธ ์ ๋ณด๊ฐ ๋ํ๋๊ฒ ๋ฉ๋๋ค.
14. ๋ง์ดํ์ด์ง์ ํ์์ ๋ณด ์์ , ์ค๋ฌธ์ ๋ณด ์์ ๋ฒํผ์ ํด๋ฆญํด ์์ ๊ธฐ์
ํ ์ ๋ณด๋ฅผ ์์ ํ ์ ์์ต๋๋ค.
15. ๋ก๊ทธ์์ ๋ฒํผ์ ๋๋ฌ ์๋ฒ์์ ์ธ์
์ ์ ์์ ์ผ๋ก ์ข
๋ฃํ ์ ์์ต๋๋ค.
16. ์์ ๊ฐ์
ํ ํ์ ์ ๋ณด๋ฅผ ํตํด ๋ก๊ทธ์ธํ์ฌ ๊ณ์ํด์ ์๋น์ค ์ด์ฉ์ด ๊ฐ๋ฅํฉ๋๋ค.
ย ๋ก๊ทธ์ธ ๊ธฐ๋ฅ :
- ํ์ด์ด๋ฒ ์ด์ค ํตํด ํ์ ๊ด๋ฆฌ๋ฅผ ํ๊ณ ์๋๋ฐ ์ถ๊ฐ๋ก ์์ ๋ก๊ทธ์ธ ๊ธฐ๋ฅ์ ๊ตฌํํ๊ธฐ
ย ํ์ ๊ฐ์ ๊ธฐ๋ฅ:
- string์ผ๋ก ๋ฐ๋ ์ฃผ์ ๋ถ๋ถ์ ์ฃผ์ API๋ฅผ ์ฌ์ฉํ์ฌ ๊ฐ์ ํ๊ธฐ
- ์ ํ๋ฒํธ, ์๋ ์์ผ string ๋ฐ๋ ๊ฒ์ด ์๋ ์ ํํ ํฌ๋งท์ผ๋ก ์ ๋ณด ์์งํ๊ธฐ
ย ์ํ๋ชฉ๋ก :
- ์ํ๋ชฉ๋ก ์นดํ ๊ณ ๋ฆฌ์ ์ํ์ ๋ค์ํํ๊ธฐ
- ์นดํ ๊ณ ๋ฆฌ ๋ฟ๋ง ์๋๋ผ ๋ฉ์ธ ํ์ด์ง์ ์ถ์ฒ ์ ํ ๋ํ๋ด๊ธฐ
ย ์ค๋ฌธ์กฐ์ฌ :
- ์๋ ๋ฅด๊ธฐ ํญ๋ชฉ ์ธ๋ถํํ์ฌ ์ ์ฉ
- ์ค๋ฌธ์กฐ์ฌ UI ๋ ๋ณด๊ธฐ ์ข๊ฒ ๊ฐ์ ํ๊ธฐ
ย ์๋จ๊ตฌ์ฑ :
- ํ์ ๋จ๋ฐฑ์ง ์ญ์ทจ๋์ ๋์ฑ Fitํ ์๋จ ๊ตฌ์ฑ ์๊ณ ๋ฆฌ์ฆ ๊ตฌํ
- ๋์ ๋ ์ ์ ๋ฐ์ดํฐ๋ฅผ ๋ฐํ์ผ๋ก ์๋จ ์ถ์ฒ AI ๋ชจ๋ธ ๊ฐ๋ฐ
ย ๋ง์ดํ์ด์ง :
- ๋จ๋ฐฑ์ง ์ญ์ทจ๋ ์กฐ์ ํ ์ ์๊ฒ ์ค์ ํ๊ธฐ
- ์ค๋ฌธ์กฐ์ฌ ์์ ๋ถ๋ถ ์์ ์ฒดํฌ ํญ๋ชฉ ๊ฐํธํ๊ฒ ๊ฐ์ ํ๊ธฐ
- 2030 ๋์์ผ๋ก ์ฌ์ฉ์ ๋ถ์์ ์ค์ ํ ๊ฒฐ๊ณผ ๋จ๋ฐฑ์ง ์๋จ ํธ์ฑ ์๋น์ค์ ๊ธ์ ์ ์ธ
๋ฐ์์ ๋ณด์๊ณ ํธ์ฑ ์๋จ์ ๋ง์ถ ๋ฐฐ๋ฌ ์๋น์ค์ ๋ํ์ฌ ๊ด์ฌ์ ๋ณด์๋ค.
- ๋ถ์ ๊ฒฐ๊ณผ์ ๋ฐ๋ผ ๊ฒฐ์ ๋ฐ ๋ฐฐ์ก ์๋น์ค์ ๋ํ ๊ธฐ๋ฅ๋ค์ ์ถ๊ฐ์ ์ผ๋ก ๊ตฌํํ๊ณ ๊ฐ์ ํด
์๋จ ์ถ์ฒ ์๋น์ค๋ฅผ ๋ฐ๊ณ ์ง์ ์ํ๊น์ง ๊ตฌ๋งคํ ์ ์๋ ํ๋ซํผ ์๋น์ค๋ก ๋ง์ ์ฌ์ฉ์์
๊ด์ฌ์ ๋ฐ์ ์ ์๋ ์ฑ์ผ๋ก ๋ฐ์ ์ํฌ ์ ์์ ๊ฒ์ด๋ผ ์์ํ๋ค.
์๋ ํ์ญ๋๊น, Team BaekGu์ ๋๋ค.
![]() |
![]() |
![]() |
![]() |
| Google Drive | Github | Zoom | Notion |





































