main #10

Merged
seonkyu.kim merged 5 commits from seonkyu.kim/AcaMate_iOS:main into main 2025-02-11 08:57:54 +00:00
84 changed files with 1213 additions and 179 deletions

View File

@ -324,7 +324,7 @@
SUPPORTED_PLATFORMS = "iphoneos iphonesimulator"; SUPPORTED_PLATFORMS = "iphoneos iphonesimulator";
SUPPORTS_MACCATALYST = NO; SUPPORTS_MACCATALYST = NO;
SUPPORTS_XR_DESIGNED_FOR_IPHONE_IPAD = NO; SUPPORTS_XR_DESIGNED_FOR_IPHONE_IPAD = NO;
SWIFT_ACTIVE_COMPILATION_CONDITIONS = "DEBUG $(inherited) DEV"; SWIFT_ACTIVE_COMPILATION_CONDITIONS = "DEBUG $(inherited) DEV LOCAL";
SWIFT_EMIT_LOC_STRINGS = YES; SWIFT_EMIT_LOC_STRINGS = YES;
SWIFT_VERSION = 5.0; SWIFT_VERSION = 5.0;
TARGETED_DEVICE_FAMILY = 1; TARGETED_DEVICE_FAMILY = 1;

View File

@ -7,9 +7,10 @@
import SwiftUI import SwiftUI
// MARK: - ACAMATE // MARK: - ACAMATE
// APPSTORE_URL : https://apps.apple.com/us/app/%EC%95%84%EC%B9%B4%EB%8D%B0%EB%AF%B8%EB%A9%94%EC%9D%B4%ED%8A%B8/id6739448113 // APPSTORE_URL : https://apps.apple.com/us/app/%EC%95%84%EC%B9%B4%EB%8D%B0%EB%AF%B8%EB%A9%94%EC%9D%B4%ED%8A%B8/id6739448113
#if DEV && LOCAL //#if DEV && LOCAL
public let API_URL: String = "http://localhost:5144" //public let API_URL: String = "http://localhost:5144"
#elseif DEV //#else
#if DEV
public let API_URL: String = "https://devacamate.ipstein.myds.me" public let API_URL: String = "https://devacamate.ipstein.myds.me"
#else #else
public let API_URL: String = "https://acamate.ipstein.myds.me" public let API_URL: String = "https://acamate.ipstein.myds.me"

View File

@ -15,10 +15,18 @@ struct CircleBtnView: View {
var body: some View { var body: some View {
if let state = vm.btnStates[id] { if let state = vm.btnStates[id] {
Button{ ZStack {
guard let action = state.action else {return} if state.isReverse {
action() Circle()
} label: { .foregroundStyle(state.backColor)
.frame(width: state.width, height: state.height)
.innerShadow(shape: Circle(), color: Color(.Text.black).opacity(0.75), blur: 8, x: 0, y: 4)
} else {
Circle()
.foregroundStyle(state.backColor)
.frame(width: state.width, height: state.height)
.shadow(color: Color(.Text.black).opacity(0.75), radius: 8, x: 4, y: 8)
}
VStack(alignment: .center, spacing: 0) { VStack(alignment: .center, spacing: 0) {
if let image = state.image { if let image = state.image {
image image
@ -27,31 +35,21 @@ struct CircleBtnView: View {
.foregroundStyle(state.foreColor) .foregroundStyle(state.foreColor)
.frame(width: state.width/2, height: state.height/2) .frame(width: state.width/2, height: state.height/2)
} }
//
if let title = state.title, let font = state.font { if let title = state.title, let font = state.font {
Text("\(title)") Text("\(title)")
.font(font) .font(font)
.lineLimit(1) .lineLimit(1)
.minimumScaleFactor(0.5) .minimumScaleFactor(0.5)
.truncationMode(.tail) .truncationMode(.tail)
.foregroundStyle(state.textColor) .foregroundStyle(state.foreColor)
// .padding()
}
}
.background {
if state.isReverse {
Circle()
.accentColor(state.backColor)
.frame(width: state.width, height: state.height)
.innerShadow(shape: Circle(), color: Color(.Text.black).opacity(0.75), blur: 8, x: 0, y: 4)
} else {
Circle()
.accentColor(state.backColor)
.frame(width: state.width, height: state.height)
.shadow(color: Color(.Text.black).opacity(0.75), radius: 8, x: 4, y: 8)
} }
} }
} }
.frame(width: state.width, height: state.height)
.onTapGesture {
guard let action = state.action else {return}
action()
}
} }
} }
} }

View File

@ -20,6 +20,7 @@ struct SimpleBtnView: View {
.font(font) .font(font)
.lineLimit(1) .lineLimit(1)
.minimumScaleFactor(0.5) .minimumScaleFactor(0.5)
.multilineTextAlignment(.center)
.truncationMode(.tail) .truncationMode(.tail)
.foregroundStyle(state.textColor) .foregroundStyle(state.textColor)
.frame(width: state.width, height: state.height) .frame(width: state.width, height: state.height)
@ -38,7 +39,9 @@ struct SimpleBtnView: View {
if let image = state.image { if let image = state.image {
image image
.resizable() .resizable()
.renderingMode(.template)
.frame(width: state.width, height: state.height) .frame(width: state.width, height: state.height)
.foregroundStyle(state.isUsable ? Color(.Second.normal) : Color(.Normal.normal))
} }
} }
.disabled(!state.isUsable) .disabled(!state.isUsable)

View File

@ -12,22 +12,23 @@ import Combine
/// ///
struct NavigationView: View { struct NavigationView: View {
@EnvironmentObject var appVM: AppViewModel @EnvironmentObject var appVM: AppViewModel
@State private var naviState : NaviState = .init(act: .NONE, path: .Intro) @State private var naviState : NaviState = .init(act: .NONE, path: .Intro)
@State private var history: [PathName] = [.Intro] @State private var history: [PathName] = [.Intro]
var body: some View { var body: some View {
ZStack { VStack(spacing: 0) {
switch naviState.path {
case .NONE: ZStack {
EmptyView() switch naviState.path {
case .Intro: case .NONE:
IntroView(naviState: $naviState) EmptyView()
case .Login : case .Intro:
LoginView(naviState: $naviState) IntroView(naviState: $naviState)
case .Main: case .Login :
MainView(naviState: $naviState) LoginView(naviState: $naviState)
case .Main:
MainView(naviState: $naviState, menuName: .Home)
}
} }
} }
.onChange(of: naviState) { old, new in .onChange(of: naviState) { old, new in
@ -43,7 +44,6 @@ struct NavigationView: View {
case .MOVE: case .MOVE:
moveHistory(path: new.path) moveHistory(path: new.path)
} }
// LOG
printLog("\(old.path) => \(new.path)") printLog("\(old.path) => \(new.path)")
showHistory() showHistory()
} }

View File

@ -0,0 +1,71 @@
//
// OffsetObservableScollView.swift
// AcaMate
//
// Created by TAnine on 2/10/25.
//
import SwiftUI
import Combine
struct OffsetObservableScrollView<Content: View>: View {
var axes: Axis.Set = .vertical
var showsIndicators: Bool = true
@Binding var scrollOffset: CGPoint
@ViewBuilder var content: (ScrollViewProxy) -> Content
@Namespace var coordinateSpaceName: Namespace.ID
init(
_ axes: Axis.Set = .vertical,
showsIndicators: Bool = true,
scrollOffset: Binding<CGPoint>,
@ViewBuilder content: @escaping (ScrollViewProxy) -> Content
) {
self.axes = axes
self.showsIndicators = showsIndicators
self._scrollOffset = scrollOffset
self.content = content
}
var body: some View {
ScrollView(axes, showsIndicators: showsIndicators) {
ScrollViewReader { scrollViewProxy in
VStack(spacing: 0) {
// anchor (id "TOP")
Color.clear
.frame(height: 1)
.id("TOP")
content(scrollViewProxy)
.overlay {
GeometryReader { geometryProxy in
Color.clear
.preference(
key: ScrollOffsetPreferenceKey.self,
value: CGPoint (
x: -geometryProxy.frame(in: .named(coordinateSpaceName)).minX,
y: -geometryProxy.frame(in: .named(coordinateSpaceName)).minY
)
)
}
}
}
}
}
.coordinateSpace(name: coordinateSpaceName)
.onPreferenceChange(ScrollOffsetPreferenceKey.self) { value in
// printLog("\(scrollOffset) -> \(value)")
scrollOffset = value
}
}
}
struct ScrollOffsetPreferenceKey: PreferenceKey {
static var defaultValue: CGPoint = .zero
static func reduce(value: inout CGPoint, nextValue: () -> CGPoint) {
value = nextValue()
}
}

View File

@ -39,6 +39,9 @@ struct IntroView: View {
.onAppear { .onAppear {
printLog("IntroView_onAppear") printLog("IntroView_onAppear")
#if LOCAL
naviState.set(act: .RESET, path: .Login)
#else
subscribeAlertAction() subscribeAlertAction()
loadVersion() loadVersion()
.sink { completion in .sink { completion in
@ -66,7 +69,7 @@ struct IntroView: View {
} }
} }
.store(in: &cancellables) .store(in: &cancellables)
#endif
} }
} }

View File

@ -0,0 +1,86 @@
//
// AttendanceView.swift
// AcaMate
//
// Created by TAnine on 2/7/25.
//
import SwiftUI
struct AttendanceBoxView: View {
@State var monthlyGroup: (Int,Int) = (5,10)//(0,0)
@State var dailyGroup: (Int,Int) = (3,4)//(0,0)
var body: some View {
DashBoardView(image: Image(.Icon.attendance),
title: "출석")
{
HStack(spacing: 4) {
AttCellView(isDaily: false, valueGroup: $monthlyGroup)
Spacer()
AttCellView(isDaily: true, valueGroup: $dailyGroup)
}
.frame(maxWidth: .infinity)
} moreAction: {
// MARK: TO-DO
//
print("123")
}
}
}
struct AttCellView: View {
@StateObject var btnVM = ButtonViewModel()
@State private var cellBtnID = UUID()
let isDaily: Bool
@Binding var valueGroup: (Int,Int)
@State private var cellText: (name: String, group: String) = ("","")
var body: some View {
HStack {
CircleBtnView(vm: btnVM, id: cellBtnID)
VStack(alignment: .leading,spacing: 0) {
HStack(alignment: .center, spacing: 2) {
Text("\(cellText.name)").font(.nps(font: .bold, size: 12))
.foregroundStyle(Color(.Text.detail))
Text("출석").font(.nps(size: 12))
.foregroundStyle(Color(.Text.detail))
}
.padding(.top,4)
HStack(alignment: .center, spacing: 2) {
Spacer()
Text("\(valueGroup.0)").font(.nps(font: .bold, size: 20))
.foregroundStyle(((Double(valueGroup.0)/Double(valueGroup.1)) < 0.7) ? Color(.Other.red) : Color(.Other.blue))
.frame(width: 28,alignment: .center)
Text("/").font(.nps(size: 12))
.foregroundStyle(Color(.Text.detail))
Text("\(valueGroup.1)").font(.nps(font: .bold, size: 20))
.foregroundStyle(Color(.Text.detail))
.frame(width: 28,alignment: .center)
Text("\(cellText.group)")
.font(.nps(size: 16))
.foregroundStyle(Color(.Text.detail))
.lineLimit(1)
.minimumScaleFactor(0.5)
.truncationMode(.tail)
}
.frame(maxWidth: .infinity)
}
}
.onAppear {
btnVM.setImage(for: cellBtnID,
newImage: isDaily ? Image(.Icon.attendanceDaily) : Image(.Icon.attendanceMonthly))
btnVM.setSize(for: cellBtnID, newWidth: 48, newHeight: 48)
btnVM.setIsReverse(for: cellBtnID, newValue: true)
btnVM.setIsSelected(for: cellBtnID, newValue: true)
if isDaily {
cellText = ("일일", "시간")
} else {
cellText = ("월간", "")
}
}
}
}

View File

@ -0,0 +1,84 @@
//
// CalendarBoxView.swift
// AcaMate
//
// Created by TAnine on 2/10/25.
//
import SwiftUI
struct CalendarBoxView: View {
@State var summaryCalDataList: [SummaryCalendar]
var body: some View {
DashBoardView(image: Image(.Icon.calendar), title: "최근 일정") {
if !summaryCalDataList.isEmpty {
VStack(spacing: 12) {
ForEach(Array(summaryCalDataList.enumerated()), id: \.offset) { index, data in
CalCellView(summaryCalData: data)
}
}
} else {
Text("최근 일정이 없습니다.")
.font(.nps(size: 20))
.foregroundStyle(Color(.Text.detail))
.lineLimit(1)
.minimumScaleFactor(0.5)
.truncationMode(.tail)
.frame(maxWidth: .infinity)
.padding([.top,.bottom],12)
.background {
RoundedRectangle(cornerRadius: 4)
.stroke(Color(.Second.normal), lineWidth: 2)
.fill(Color(.Second.light))
}
}
} moreAction: {
printLog("최근일정의 더보기")
}
}
}
struct CalCellView: View {
var summaryCalData: SummaryCalendar
var body: some View {
VStack(spacing: 8) {
HStack (spacing: 12) {
Image(.Icon.clock)
.resizable()
.frame(width: 24, height: 24, alignment: .center)
Text("\(summaryCalData.date)")
.font(.nps(size: 20))
.foregroundStyle(Color(.Text.detail))
Spacer(minLength: 1)
}
HStack(spacing: 0) {
Spacer(minLength: 1)
Text("\(summaryCalData.summary)")
.font(.nps(size: 20))
.foregroundStyle(Color(.Text.black))
.lineLimit(1)
.minimumScaleFactor(0.5)
.truncationMode(.tail)
}
}
.padding(12)
.background {
RoundedRectangle(cornerRadius: 4)
.stroke(Color(.Second.normal), lineWidth: 2)
.fill(Color(.Second.light))
}
.onTapGesture {
printLog("캘린더 내부 셀 클릭")
// MARK: TO-DO
// Summary
// SummaryCalendar' id
//
}
}
}

View File

@ -0,0 +1,53 @@
//
// DashBoardView.swift
// AcaMate
//
// Created by TAnine on 2/10/25.
//
import SwiftUI
struct DashBoardView<Content: View>: View {
@StateObject var btnVM = ButtonViewModel()
@State private var attMoreBtnID = UUID()
let image: Image
let title: String
@ViewBuilder let content: Content
var moreAction: VOID_TO_VOID?
var body: some View {
VStack(spacing: 0) {
HStack(spacing: 0){
image
.resizable()
.frame(width: 24, height: 24, alignment: .center)
.padding([.trailing],4)
Text("\(title)")
.font(.nps(font: .bold, size: 20))
.foregroundStyle(Color(.Text.detail))
Spacer()
SimpleBtnView(vm: btnVM, id: attMoreBtnID)
}
.padding([.bottom],2)
Rectangle()
.frame(maxWidth: .infinity, maxHeight: 2)
.foregroundStyle(Color(.Second.normal))
.padding([.bottom],12)
content
}
.padding(24)
.onAppear {
btnVM.setSize(for: attMoreBtnID, newWidth: 40, newHeight: 24)
btnVM.setText(for: attMoreBtnID, newText: "더보기", newFont: .nps(size: 12))
btnVM.setTextColor(for: attMoreBtnID, newColor: .Text.disabled)
if let action = moreAction {
btnVM.setAction(for: attMoreBtnID, newAction: action)
}
}
}
}

View File

@ -0,0 +1,72 @@
//
// HomeView.swift
// AcaMate
//
// Created by TAnine on 2/11/25.
//
import SwiftUI
struct HomeView: View {
@State private var scrollOffset: CGPoint = .zero
@State private var topViewState: Bool = false
var body: some View {
VStack(spacing: 0) {
ZStack {
OffsetObservableScrollView(showsIndicators: false, scrollOffset: $scrollOffset) { proxy in
VStack(spacing: 0) {
TopProfileView(userType: .Student)
// .padding(EdgeInsets(top: 0, leading: 0, bottom: 12, trailing: 0))s
.padding(.bottom,12)
Group {
AttendanceBoxView()
CalendarBoxView(summaryCalDataList: [])
CalendarBoxView(summaryCalDataList: [
SummaryCalendar(id: "123", date: "2025-02-28", summary: "요약내용입니다."),
SummaryCalendar(id: "123", date: "2025-02-28", summary: "요약내용입니다.")])
ManagementBoxView(managementList: [])
ManagementBoxView(managementList: [
SummaryManagement(id: "01", title: "과목 명1", teacher: "A", ratio: 27, homework: 3),
SummaryManagement(id: "02", title: "과목 명2", teacher: "B", ratio: 80, homework: 10),
SummaryManagement(id: "03", title: "과목 명3", teacher: "C", ratio: 13, homework: 2),
SummaryManagement(id: "04", title: "과목 명4", teacher: "D", ratio: 72, homework: 0),
])
NoticeBoxView(noticeList: [
SummaryNotice(id: "00", title: "공지사항1", date: "2025-02-11", new: true),
SummaryNotice(id: "01", title: "공지사항2", date: "2025-01-11", new: false),
SummaryNotice(id: "02", title: "공지사항3", date: "2025-01-01", new: false)
])
}
.background {
RoundedRectangle(cornerRadius: 8)
.foregroundStyle(Color(.Other.cell))
}
.padding(EdgeInsets(top: 12, leading: 24, bottom: 12, trailing: 24))
}
}
if topViewState {
VStack(spacing: 0) {
TopView(titleName: "Name")
.transition(.move(edge: .top))
.animation(.easeInOut, value: scrollOffset)
Spacer(minLength: 1)
}
}
}
}
.frame(maxWidth: .infinity, maxHeight: .infinity)
.onChange(of: scrollOffset.y) { oldValue, newValue in
if newValue > 200 && topViewState == false {
topViewState = true
} else if newValue < 20 && topViewState == true{
topViewState = false
}
}
}
}

View File

@ -0,0 +1,173 @@
//
// ManagementBoxView.swift
// AcaMate
//
// Created by TAnine on 2/10/25.
//
import SwiftUI
struct ManagementBoxView: View {
@StateObject var btnVM = ButtonViewModel()
@State private var leftBtnID = UUID()
@State private var rightBtnID = UUID()
@State var managementList: [SummaryManagement]
@State private var countNum: Int = 0
var body: some View {
DashBoardView(image: Image(.Icon.edu), title: "학습 관리") {
if managementList.count >= 1 {
HStack {
SimpleBtnView(vm: btnVM, id: leftBtnID)
Spacer(minLength: 1)
SimpleBtnView(vm: btnVM, id: rightBtnID)
}
.padding([.bottom],12)
MangCellView(summaryMang: $managementList[countNum])
} else {
Text("학습하고 있는 강의가 없습니다.")
.font(.nps(size: 20))
.foregroundStyle(Color(.Text.detail))
.lineLimit(1)
.minimumScaleFactor(0.5)
.truncationMode(.tail)
.frame(maxWidth: .infinity)
.padding([.top,.bottom],12)
.background {
RoundedRectangle(cornerRadius: 4)
.stroke(Color(.Second.normal), lineWidth: 2)
.fill(Color(.Second.light))
}
}
} moreAction: {
// MARK: TO-DO
//
printLog("학습 관리의 더보기")
}
.onAppear {
btnVM.setImage(for: leftBtnID, newImage: Image(.Icon.left))
btnVM.setSize(for: leftBtnID, newWidth: 24, newHeight: 24)
btnVM.setAction(for: leftBtnID) { countNum -= 1 }
btnVM.setIsUsable(for: leftBtnID, newValue: false)
btnVM.setImage(for: rightBtnID, newImage: Image(.Icon.right))
btnVM.setSize(for: rightBtnID, newWidth: 24, newHeight: 24)
btnVM.setAction(for: rightBtnID) { countNum += 1 }
if managementList.count == 0 {
btnVM.setIsUsable(for: rightBtnID, newValue: false)
}
}
.onChange(of: countNum) { oldValue, newValue in
if countNum == 0 {
btnVM.setIsUsable(for: leftBtnID, newValue: false)
} else if 0 < countNum && countNum < managementList.count-1 {
btnVM.setIsUsable(for: leftBtnID, newValue: true)
btnVM.setIsUsable(for: rightBtnID, newValue: true)
}
else {
btnVM.setIsUsable(for: rightBtnID, newValue: false)
}
}
}
}
struct MangCellView: View {
@StateObject var btnVM = ButtonViewModel()
@Binding var summaryMang: SummaryManagement
@State var ratioBtn = UUID()
@State var flagBtn = UUID()
var body: some View {
VStack(spacing: 0) {
VStack(spacing: 8) {
HStack(spacing: 12) {
Image(.Icon.management)
.resizable()
.frame(width: 24, height: 24, alignment: .center)
Text("\(summaryMang.title)")
.font(.nps(size: 20))
.foregroundStyle(Color(.Text.title))
Spacer(minLength: 1)
}
HStack(spacing: 4) {
Spacer(minLength: 1)
Text("\(summaryMang.teacher)")
.font(.nps(size: 12))
.foregroundStyle(Color(.Text.detail))
Text("선생님")
.font(.nps(size: 12))
.foregroundStyle(Color(.Text.detail))
}
}
//
HStack(spacing: 6) {
HStack(spacing: 10) {
CircleBtnView(vm: btnVM, id: ratioBtn)
VStack(spacing: 10) {
HStack(spacing: 4) {
Text("학습")
.font(.nps(size: 16))
.foregroundStyle(Color(.Text.detail))
Text("진도")
.font(.nps(font: .bold, size: 16))
.foregroundStyle(Color(.Text.detail))
Spacer(minLength: 1)
}
HStack(spacing: 4) {
Spacer(minLength: 1)
Text("\(summaryMang.ratio)")
.font(.nps(font: .bold, size: 24))
.foregroundStyle(summaryMang.ratio > 70 ? Color(.Other.blue):Color(.Other.red))
Text("%")
.font(.nps(font: .bold, size: 20))
.foregroundStyle(Color(.Text.detail))
}
}
VStack(spacing: 10) {
HStack(spacing: 4) {
Text("숙제")
.font(.nps(font: .bold, size: 16))
.foregroundStyle(Color(.Text.detail))
Text("개수")
.font(.nps(size: 16))
.foregroundStyle(Color(.Text.detail))
Spacer(minLength: 1)
}
HStack(spacing: 4) {
Spacer(minLength: 1)
Text("\(summaryMang.homework)")
.font(.nps(font: .bold, size: 24))
.foregroundStyle(Color(.Second.normal))
Text("")
.font(.nps(font: .bold, size: 20))
.foregroundStyle(Color(.Text.detail))
}
}
}
}
}
.padding(12)
.background {
RoundedRectangle(cornerRadius: 4)
.stroke(Color(.Second.normal), lineWidth: 2)
.fill(Color(.Second.light))
}
.onTapGesture {
// MARK: TO-DO
//
printLog(summaryMang)
}
}
}

View File

@ -0,0 +1,58 @@
//
// NoticeBoxView.swift
// AcaMate
//
// Created by TAnine on 2/11/25.
//
import SwiftUI
struct NoticeBoxView: View {
@State var noticeList: [SummaryNotice]
var body: some View {
DashBoardView(image: Image(.Icon.notice), title: "공지사항") {
ForEach(Array(noticeList.enumerated()), id: \.offset) { index, notice in
if index < 3 {
NotiCellView(summaryNoti: notice)
}
}
//
} moreAction: {
// MARK: TO-DO
//
printLog("공지사항의 더보기")
}
}
}
struct NotiCellView: View {
var summaryNoti: SummaryNotice
var body: some View {
VStack(spacing: 8) {
HStack(spacing: 12) {
Image(summaryNoti.new ? .Icon.noticeNew : .Icon.noticeOld)
.resizable()
.frame(width: 24, height: 24, alignment: .center)
Text("\(summaryNoti.title)")
.font(.nps(size: 20))
.foregroundStyle(Color(.Text.title))
Spacer(minLength: 1)
}
HStack(spacing: 4) {
Spacer(minLength: 1)
Text("날짜 :")
.font(.nps(size: 12))
.foregroundStyle(Color(.Text.detail))
Text("\(summaryNoti.date)")
.font(.nps(size: 12))
.foregroundStyle(Color(.Text.detail))
}
}
}
}

View File

@ -0,0 +1,136 @@
//
// TopProfileView.swift
// AcaMate
//
// Created by TAnine on 2/5/25.
//
import SwiftUI
struct TopProfileView: View {
@StateObject var btnVM = ButtonViewModel()
var userType: UserType
// MARK: TO-DO
//
var childrenList: [String] = ["name1", "name2", "name3"]
var academyName: String = "Academy' NAME"
var myName: String = "Name"
@State private var typeName: String = "유형"
@State private var ParentsSelectID: UUID?
@State private var shopID = UUID()
@State private var notifyID = UUID()
@State private var childIDList: [UUID] = []
var body: some View {
VStack(spacing: 0){
VStack(alignment: .center, spacing: 0) {
HStack(spacing: 0) {
SimpleBtnView(vm: btnVM, id: shopID)
Spacer(minLength: 1)
SimpleBtnView(vm: btnVM, id: notifyID)
} /// Stack
ZStack{
Circle()
.stroke(Color(.Second.normal) ,lineWidth: 4)
.frame(width: 200, height: 200)
Text("\(typeName)")
.font(.nps(font: .bold, size: 48))
.foregroundStyle(Color(.Second.normal))
} /// Stack
.padding([.top, .bottom], 40)
VStack(alignment: .center, spacing: 8) {
Text("\(self.academyName)")
.frame(alignment: .center)
.font(.nps(font: .bold, size: 36))
.foregroundStyle(Color(.Text.title))
.lineLimit(1)
.minimumScaleFactor(0.5)
.truncationMode(.tail)
Text("\(self.myName)")
.multilineTextAlignment(.center)
.frame(alignment: .center)
.font(.nps(size: 18))
.foregroundStyle(Color(.Text.detail))
.lineLimit(1)
.minimumScaleFactor(0.5)
.truncationMode(.tail)
}
} /// VStack
.padding(EdgeInsets(top: 24, leading: 24, bottom: 12, trailing: 24))
// MARK: TO-DO
//
if userType == .Parent {
HStack(spacing: 0) {
ForEach(Array(childIDList.enumerated()),id: \.offset){ index, id in
CircleBtnView(vm: btnVM, id: id)
if index != childIDList.count-1 {
Spacer()
}
}
} /// HStack
.padding([.leading, .trailing, .bottom], 24)
}
}
.fullDrawView(.Other.cell)
.onAppear {
let topBtnIDList = [shopID,notifyID]
let iconList = [Image(.Icon.market), Image(.Icon.notificationSET)]
topBtnIDList.enumerated().forEach { (index, id) in
btnVM.setImage(for: topBtnIDList[index], newImage: iconList[index])
btnVM.setSize(for: topBtnIDList[index], newWidth: 40, newHeight: 40)
}
// MARK: TO-DO
//
switch self.userType {
case .Student:
typeName = "학생"
case .Parent:
typeName = "학부모"
for _ in 0..<childrenList.count {
childIDList.append(UUID())
}
ParentsSelectID = childIDList[0]
btnVM.setIsSelected(for: childIDList[0], newValue: true)
childIDList.enumerated().forEach { (index, id) in
btnVM.setImage(for: id, newImage: Image(.Icon.face))
btnVM.setSize(for: id, newWidth: 64, newHeight: 64)
btnVM.setText(for: id, newText: "\(childrenList[index])", newFont: .nps(size: 12))
// MARK: TO-DO
//
btnVM.setAction(for: id) {
if let selectID = ParentsSelectID {
btnVM.setIsSelected(for: selectID, newValue: false)
}
btnVM.setIsSelected(for: id, newValue: true)
ParentsSelectID = id
}
}
case .Teacher:
typeName = "선생님"
case .Admin:
typeName = "관리자"
case .Employee:
typeName = "직원"
case .ETC:
typeName = "방문객"
}
}
}
}

View File

@ -1,106 +0,0 @@
//
// TopProfileView.swift
// AcaMate
//
// Created by TAnine on 2/5/25.
//
import SwiftUI
struct TopProfileView: View {
var userType: UserType = .ETC
var childrenList: [String] = ["name1", "name2", "name3"]
var academyName: String = "Academy' NAME"
var myName: String = "Name"
@State var childNum: Int = 0
var body: some View {
VStack(spacing: 0){
VStack(alignment: .center, spacing: 0) {
HStack(spacing: 0) {
// SimpleBtnView(image: Image(.Icon.market), title: nil, font: nil,
// width: 40, height: 40)
// .doAction {
// printLog("TEST")
// }
Spacer(minLength: 1)
// SimpleBtnView(image: Image(.Icon.notificationSET), title: nil, font: nil,
// width: 40, height: 40)
// .doAction {
// printLog("TEST")
// }
} /// Stack
ZStack{
Circle()
.stroke(Color(.Second.normal) ,lineWidth: 4)
.frame(width: 200, height: 200)
switch self.userType {
case .Student:
// MARK: TO-DO
//
Image(.Icon.face)
.resizable()
.frame(width: 150, height: 150)
case .Parent:
Text("학부모")
.font(.nps(font: .bold, size: 48))
.foregroundStyle(Color(.Second.normal))
case .Teacher:
Text("선생님")
.font(.nps(font: .bold, size: 48))
.foregroundStyle(Color(.Second.normal))
case .Admin:
Text("관리자")
.font(.nps(font: .bold, size: 48))
.foregroundStyle(Color(.Second.normal))
case .Employee:
Text("직원")
.font(.nps(font: .bold, size: 48))
.foregroundStyle(Color(.Second.normal))
case .ETC:
Text("방문객")
.font(.nps(font: .bold, size: 48))
.foregroundStyle(Color(.Second.normal))
}
} /// Stack
.padding([.top, .bottom], 40)
VStack(alignment: .center, spacing: 8) {
Text("\(self.academyName)")
.frame(alignment: .center)
.multilineTextAlignment(.center)
.font(.nps(font: .bold, size: 36))
Text("\(self.myName)")
.multilineTextAlignment(.center)
.frame(alignment: .center)
.font(.nps(size: 18))
.foregroundStyle(Color(.Text.detail))
}
} /// VStack
.padding(EdgeInsets(top: 24, leading: 24, bottom: 12, trailing: 24))
HStack(spacing: 0) {
// ForEach(Array(childrenList.enumerated()),id: \.offset){ index, name in
// CircleBtnView(title: "\(name)", image: Image(.Icon.face),
// // MARK: TO-DO
// ///
// isSelected: Binding(
// get: { self.childNum == index },
// set: { _ in self.childNum = index }
// ), isReverse: false) {
// self.childNum = index
// }
// if index != childrenList.count-1 {
// Spacer()
// }
// }
} /// HStack
.padding([.leading, .trailing], 24)
}
.fullDrawView(.Other.cell)
}
}
#Preview {
TopProfileView()
}

View File

@ -0,0 +1,14 @@
//
// ManagementView.swift
// AcaMate
//
// Created by TAnine on 2/11/25.
//
import SwiftUI
struct ManagementView: View {
var body: some View {
Text("학습 관리")
}
}

View File

@ -0,0 +1,14 @@
//
// ChattingView.swift
// AcaMate
//
// Created by TAnine on 2/11/25.
//
import SwiftUI
struct ChattingView: View {
var body: some View {
Text("채팅")
}
}

View File

@ -0,0 +1,14 @@
//
// CalendarView.swift
// AcaMate
//
// Created by TAnine on 2/11/25.
//
import SwiftUI
struct CalendarView: View {
var body: some View {
Text("일정")
}
}

View File

@ -0,0 +1,15 @@
//
// EtcView.swift
// AcaMate
//
// Created by TAnine on 2/11/25.
//
import SwiftUI
struct EtcView: View {
var body: some View {
Text("더보기")
}
}

View File

@ -17,6 +17,7 @@ struct BottomView: View {
@State private var calendarID = UUID() @State private var calendarID = UUID()
@State private var etcID = UUID() @State private var etcID = UUID()
@Binding var menuName: MenuName
var body: some View { var body: some View {
let idList: [UUID] = [homeID,managementID,chattingID,calendarID,etcID] let idList: [UUID] = [homeID,managementID,chattingID,calendarID,etcID]
@ -29,11 +30,7 @@ struct BottomView: View {
} }
} }
// MARK: TO-DO .padding(EdgeInsets(top: 12, leading: 24, bottom: 0, trailing: 24))
//
.padding([.top],12)
.padding([.horizontal],24)
// .padding(EdgeInsets(top: 12, leading: 24, bottom: 0, trailing: 24))
.background { .background {
Rectangle() Rectangle()
@ -43,13 +40,14 @@ struct BottomView: View {
.frame(maxWidth: .infinity) .frame(maxWidth: .infinity)
.onAppear { .onAppear {
let idList: [UUID] = [homeID,managementID,chattingID,calendarID,etcID] let idList: [UUID] = [homeID,managementID,chattingID,calendarID,etcID]
let btnText: [String] = ["", "학습 관리", "채팅", "일정", "더보기"] //["", " ", "", "", ""]
let btnType: [MenuName] = [.Home, .Management, .Chatting, .Calendar, .Etc]
let btnImage: [Image] = [Image(.Icon.home),Image(.Icon.management),Image(.Icon.chatting),Image(.Icon.calendar),Image(.Icon.etc)] let btnImage: [Image] = [Image(.Icon.home),Image(.Icon.management),Image(.Icon.chatting),Image(.Icon.calendar),Image(.Icon.etc)]
idList.enumerated().forEach { (index, id) in idList.enumerated().forEach { (index, id) in
btnVM.btnStates[id] = ButtonState() btnVM.btnStates[id] = ButtonState()
btnVM.setSize(for: id, newWidth: 48, newHeight: 48) btnVM.setSize(for: id, newWidth: 52, newHeight: 52)
btnVM.setText(for: id, newText: btnText[index], btnVM.setText(for: id, newText: btnType[index].rawValue,
newFont: .nps(font: .bold, size: 6)) newFont: .nps(font: .bold, size: 6))
btnVM.setImage(for: id, newImage: btnImage[index]) btnVM.setImage(for: id, newImage: btnImage[index])
@ -60,6 +58,7 @@ struct BottomView: View {
btnVM.setIsSelected(for: $0, newValue: false) btnVM.setIsSelected(for: $0, newValue: false)
} }
} }
menuName = btnType[index]
} }
} }

View File

@ -13,22 +13,44 @@ struct MainView: View {
@State var cancellables: Set<AnyCancellable> = [] @State var cancellables: Set<AnyCancellable> = []
@Binding var naviState : NaviState @Binding var naviState : NaviState
@State var menuName: MenuName
// @State private var scrollOffset: CGPoint = .zero
// @State private var topViewState: Bool = false
var body: some View { var body: some View {
VStack(spacing: 0) { VStack(spacing: 0) {
TopView(titleName: "Name") Group {
switch menuName {
case .Home:
HomeView()
case .Management:
ManagementView()
case .Chatting:
ChattingView()
case .Calendar:
CalendarView()
case .Etc:
EtcView()
}
}
Spacer(minLength: 1)
Spacer() BottomView(menuName: $menuName)
BottomView()
.frame(maxWidth: .infinity) .frame(maxWidth: .infinity)
// .backgroundStyle(Color(.Normal.dark))
// .safeAreaInset(edge: .bottom, spacing: 0) {
// Color(.Normal.dark)
//// .frame(height: 0)
// }
} }
// .fullDrawView(Color(.Normal.dark)) .frame(maxWidth: .infinity, maxHeight: .infinity)
//
// .onChange(of: scrollOffset.y) { oldValue, newValue in
// if newValue > 200 && topViewState == false {
// topViewState = true
// } else if newValue < 20 && topViewState == true{
// topViewState = false
// }
// }
} }
} }

View File

@ -11,13 +11,12 @@ struct TopView: View {
@StateObject var btnVM = ButtonViewModel() @StateObject var btnVM = ButtonViewModel()
@State var titleName: String = "" @State var titleName: String = ""
@State var changeLogo: Bool = false
@State var tailLogo: Bool = false
@State private var leftBtnID = UUID() @State private var leftBtnID = UUID()
@State private var rightBtnID = UUID() @State private var rightBtnID = UUID()
var myType: UserType = .Teacher //MARK: -
var myType: UserType = .Student
var body: some View { var body: some View {
HStack(alignment: .center, spacing: 0) { HStack(alignment: .center, spacing: 0) {
@ -36,6 +35,7 @@ struct TopView: View {
.foregroundStyle(Color(.Text.detail)) .foregroundStyle(Color(.Text.detail))
.font(.nps(font: .bold, size: 20)) .font(.nps(font: .bold, size: 20))
Spacer() Spacer()
SimpleBtnView(vm: btnVM, id: rightBtnID) SimpleBtnView(vm: btnVM, id: rightBtnID)
.padding(EdgeInsets(top: 12, leading: 12, bottom: 12, trailing: 24)) .padding(EdgeInsets(top: 12, leading: 12, bottom: 12, trailing: 24))

View File

@ -21,6 +21,8 @@ struct ButtonState {
var textColor: Color = .Text.detail var textColor: Color = .Text.detail
var isUsable: Bool = true var isUsable: Bool = true
// -- CircleBtn -- // // -- CircleBtn -- //
var isSelected: Bool = false var isSelected: Bool = false

View File

@ -0,0 +1,14 @@
//
// SimpleCalendar.swift
// AcaMate
//
// Created by TAnine on 2/10/25.
//
import Foundation
struct SummaryCalendar {
var id: String
var date: String
var summary: String
}

View File

@ -0,0 +1,16 @@
//
// ManagementData.swift
// AcaMate
//
// Created by TAnine on 2/10/25.
//
import Foundation
struct SummaryManagement {
var id: String
var title: String
var teacher: String
var ratio: Int
var homework: Int
}

View File

@ -13,6 +13,8 @@ struct NaviState: Equatable {
var act: NaviAction var act: NaviAction
var path: PathName var path: PathName
static func == (lhs: NaviState, rhs: NaviState) -> Bool { static func == (lhs: NaviState, rhs: NaviState) -> Bool {
return lhs.act == rhs.act && lhs.path == rhs.path return lhs.act == rhs.act && lhs.path == rhs.path
} }
@ -21,6 +23,7 @@ struct NaviState: Equatable {
self.act = act self.act = act
self.path = path self.path = path
} }
} }
enum NaviAction: Hashable { enum NaviAction: Hashable {
@ -45,3 +48,11 @@ enum PathName: Hashable {
case NONE case NONE
} }
enum MenuName: String, Hashable {
case Home = ""
case Management = "학습 관리"
case Chatting = "채팅"
case Calendar = "일정"
case Etc = "더보기"
}

View File

@ -0,0 +1,15 @@
//
// Notice Data.swift
// AcaMate
//
// Created by TAnine on 2/11/25.
//
import Foundation
struct SummaryNotice {
var id: String
var title: String
var date: String
var new: Bool
}

View File

@ -10,7 +10,6 @@ import Combine
class AppViewModel: ObservableObject { class AppViewModel: ObservableObject {
@Published var isLoading: Bool = false @Published var isLoading: Bool = false
@Published var showAlert: Bool = false @Published var showAlert: Bool = false
var alertData: AlertData = .init(body: "") var alertData: AlertData = .init(body: "")

View File

@ -60,6 +60,13 @@ class ButtonViewModel: ObservableObject {
btnStates[id] = state btnStates[id] = state
objectWillChange.send() objectWillChange.send()
} }
//
// func setDisable(for id: UUID, _ newValue: Bool) {
// var state = btnStates[id] ?? ButtonState()
// state.isUsable = newValue
// btnStates[id] = state
// objectWillChange.send()
// }
// -- CircleBtn -- // // -- CircleBtn -- //
@ -74,7 +81,11 @@ class ButtonViewModel: ObservableObject {
func setIsReverse(for id: UUID, newValue: Bool){ func setIsReverse(for id: UUID, newValue: Bool){
var state = btnStates[id] ?? ButtonState() var state = btnStates[id] ?? ButtonState()
state.isReverse = newValue state.isReverse = newValue
state.backColor = newValue ? Color(.Second.light) : Color(.Normal.normal) if state.isReverse {
state.backColor = Color(.Second.light)
} else {
state.backColor = Color(.Normal.normal)
}
btnStates[id] = state btnStates[id] = state
objectWillChange.send() objectWillChange.send()
} }

View File

@ -72,6 +72,42 @@ struct LoadingModifier: ViewModifier {
} }
} }
struct PressEffect: ViewModifier {
@State private var isPressed = false
var scale: CGFloat
var opacity: CGFloat
var duration: Double
func body(content: Content) -> some View {
content
.scaleEffect(isPressed ? scale : 1.0)
.opacity(isPressed ? opacity : 1.0)
.animation(.easeOut(duration: duration), value: isPressed)
.simultaneousGesture(
LongPressGesture(minimumDuration: 0.01)
.onChanged { _ in isPressed = true }
.onEnded { _ in isPressed = false }
)
}
}
struct PressBackgroundEffect: ViewModifier {
@State private var isPressed = false
var backgroundColor: Color
var duration: Double
func body(content: Content) -> some View {
content
.background(isPressed ? backgroundColor : Color.clear)
.animation(.easeOut(duration: duration), value: isPressed)
.simultaneousGesture(
LongPressGesture(minimumDuration: 0.01)
.onChanged { _ in isPressed = true }
.onEnded { _ in isPressed = false }
)
}
}
extension View { extension View {
/// View /// View
func fullDrawView(_ backColor: Color) -> some View { func fullDrawView(_ backColor: Color) -> some View {
@ -104,16 +140,14 @@ extension View {
.mask(shape.fill(LinearGradient(gradient: Gradient(colors: [.black, .clear]), startPoint: .topLeading, endPoint: .bottomTrailing))) .mask(shape.fill(LinearGradient(gradient: Gradient(colors: [.black, .clear]), startPoint: .topLeading, endPoint: .bottomTrailing)))
} }
} }
@ViewBuilder func pressAnimation(scale: CGFloat = 0.95, opacity: CGFloat = 0.85, duration: Double = 0.1) -> some View {
func switchButtonStyle(_ animate: Bool) -> some View { self.modifier(PressEffect(scale: scale, opacity: opacity, duration: duration))
if animate {
self.buttonStyle(DefaultButtonStyle()) }
} else { }
self.buttonStyle(PlainButtonStyle()) extension View {
// .allowsHitTesting(false) func pressColorAnimation(backgroundColor: Color = Color.black.opacity(0.1), duration: Double = 0.1) -> some View {
} self.modifier(PressBackgroundEffect(backgroundColor: backgroundColor, duration: duration))
} }
} }
//extension BUtton

View File

@ -0,0 +1,20 @@
{
"colors" : [
{
"color" : {
"color-space" : "srgb",
"components" : {
"alpha" : "1.000",
"blue" : "0.114",
"green" : "0.114",
"red" : "0.114"
}
},
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 741 B

View File

@ -0,0 +1,12 @@
{
"images" : [
{
"filename" : "Attendance.png",
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 779 B

View File

@ -0,0 +1,12 @@
{
"images" : [
{
"filename" : "Attendance_Daily.png",
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 630 B

View File

@ -0,0 +1,12 @@
{
"images" : [
{
"filename" : "Attendance_Monthly.png",
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

View File

@ -0,0 +1,15 @@
{
"images" : [
{
"filename" : "Clock.png",
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
},
"properties" : {
"preserves-vector-representation" : true
}
}

View File

@ -0,0 +1,12 @@
{
"images" : [
{
"filename" : "Drive OFF.png",
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

View File

@ -0,0 +1,12 @@
{
"images" : [
{
"filename" : "Drive ON.png",
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

View File

@ -0,0 +1,12 @@
{
"images" : [
{
"filename" : "Drive.png",
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

View File

@ -0,0 +1,15 @@
{
"images" : [
{
"filename" : "Edu.png",
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
},
"properties" : {
"preserves-vector-representation" : true
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

View File

@ -0,0 +1,12 @@
{
"images" : [
{
"filename" : "Flag.png",
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 463 B

View File

@ -0,0 +1,15 @@
{
"images" : [
{
"filename" : "Left.png",
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
},
"properties" : {
"preserves-vector-representation" : true
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 505 B

View File

@ -0,0 +1,12 @@
{
"images" : [
{
"filename" : "Notice.png",
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

View File

@ -0,0 +1,12 @@
{
"images" : [
{
"filename" : "Notice_New.png",
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 618 B

View File

@ -0,0 +1,12 @@
{
"images" : [
{
"filename" : "Notice_Old.png",
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 579 B

View File

@ -0,0 +1,12 @@
{
"images" : [
{
"filename" : "Ratio.png",
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

View File

@ -0,0 +1,15 @@
{
"images" : [
{
"filename" : "Right.png",
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
},
"properties" : {
"preserves-vector-representation" : true
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 470 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 741 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 779 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 630 B

View File

Before

Width:  |  Height:  |  Size: 636 B

After

Width:  |  Height:  |  Size: 636 B

View File

Before

Width:  |  Height:  |  Size: 543 B

After

Width:  |  Height:  |  Size: 543 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

View File

Before

Width:  |  Height:  |  Size: 635 B

After

Width:  |  Height:  |  Size: 635 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 463 B

View File

Before

Width:  |  Height:  |  Size: 604 B

After

Width:  |  Height:  |  Size: 604 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 505 B

View File

Before

Width:  |  Height:  |  Size: 577 B

After

Width:  |  Height:  |  Size: 577 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 618 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 579 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 470 B

View File

@ -11,3 +11,13 @@
2. kakao Login 2. kakao Login
3. naver Map 3. naver Map
4. firebase 4. firebase
## Git Commit message
1. [📝]: DOCS -> 문서 추가, 문서 수정 등
2. [✨]: FEAT -> 새로운 기능의 추가
3. [🔥]: FIRE -> 코드나 문서 등의 삭제
4. [🐛]: FIX BUG -> 버그 수정
5. [👷]: STYLE -> 직접적인 코드의 수정은 없는 경우 (빈줄 처리, 주석 등)
6. [♻️]: REFACTOR -> 코드 리팩토링
7. [✅]: TEST -> 테스트 코드 추가
8. [📁]: CHORE: 빌드 업무, 패키지 매니저 수정 등 (.gitignore 수정 등)