diff --git a/SodaLive/Sources/App/AppStep.swift b/SodaLive/Sources/App/AppStep.swift
index fc6d484..4498ead 100644
--- a/SodaLive/Sources/App/AppStep.swift
+++ b/SodaLive/Sources/App/AppStep.swift
@@ -142,7 +142,7 @@ enum AppStep {
     
     case auditionRoleDetail(roleId: Int, auditionTitle: String)
     
-    case searchChannel
+    case search
     
     case contentMain(startTab: ContentMainTab)
     
diff --git a/SodaLive/Sources/Content/Main/ContentMainView.swift b/SodaLive/Sources/Content/Main/ContentMainView.swift
index edb9df7..2c509d1 100644
--- a/SodaLive/Sources/Content/Main/ContentMainView.swift
+++ b/SodaLive/Sources/Content/Main/ContentMainView.swift
@@ -61,7 +61,7 @@ struct ContentMainView: View {
                         .padding(.horizontal, 13.3)
                         .onTapGesture {
                             UserDefaults.set("", forKey: .searchChannel)
-                            AppState.shared.setAppStep(step: .searchChannel)
+                            AppState.shared.setAppStep(step: .search)
                         }
                         
                         ContentMainCreatorRankingView()
diff --git a/SodaLive/Sources/Content/Main/V2/Home/ContentMainTabHomeView.swift b/SodaLive/Sources/Content/Main/V2/Home/ContentMainTabHomeView.swift
index 19a0393..45b26b9 100644
--- a/SodaLive/Sources/Content/Main/V2/Home/ContentMainTabHomeView.swift
+++ b/SodaLive/Sources/Content/Main/V2/Home/ContentMainTabHomeView.swift
@@ -81,7 +81,7 @@ struct ContentMainTabHomeView: View {
                             .padding(.horizontal, 13.3)
                             .onTapGesture {
                                 UserDefaults.set("", forKey: .searchChannel)
-                                AppState.shared.setAppStep(step: .searchChannel)
+                                AppState.shared.setAppStep(step: .search)
                             }
                             
                             VStack(spacing: 13.3) {
diff --git a/SodaLive/Sources/Content/Series/Detail/SeriesDetailView.swift b/SodaLive/Sources/Content/Series/Detail/SeriesDetailView.swift
index de6893f..bd82e48 100644
--- a/SodaLive/Sources/Content/Series/Detail/SeriesDetailView.swift
+++ b/SodaLive/Sources/Content/Series/Detail/SeriesDetailView.swift
@@ -10,6 +10,7 @@ import Kingfisher
 
 struct SeriesDetailView: View {
     
+    @Environment(\.presentationMode) var presentationMode: Binding<PresentationMode>
     @ObservedObject var viewModel = SeriesDetailViewModel()
     
     let seriesId: Int
@@ -35,7 +36,13 @@ struct SeriesDetailView: View {
                                 Image("ic_back")
                                     .resizable()
                                     .frame(width: 20, height: 20)
-                                    .onTapGesture { AppState.shared.back() }
+                                    .onTapGesture {
+                                        if presentationMode.wrappedValue.isPresented {
+                                            presentationMode.wrappedValue.dismiss()
+                                        } else {
+                                            AppState.shared.back()
+                                        }
+                                    }
                                 
                                 Spacer()
                             }
@@ -232,6 +239,8 @@ struct SeriesDetailView: View {
                     }
                 }
             }
+            .navigationTitle("")
+            .navigationBarBackButtonHidden()
         }
         .onAppear {
             viewModel.seriesId = seriesId
diff --git a/SodaLive/Sources/ContentView.swift b/SodaLive/Sources/ContentView.swift
index 6626fa8..e05aed3 100644
--- a/SodaLive/Sources/ContentView.swift
+++ b/SodaLive/Sources/ContentView.swift
@@ -215,8 +215,8 @@ struct ContentView: View {
                     auditionTitle: auditionTitle
                 )
                 
-            case .searchChannel:
-                SearchChannelView()
+            case .search:
+                SearchView()
                 
             case .contentMain(let startTab):
                 ContentMainViewV2(selectedTab: startTab)
diff --git a/SodaLive/Sources/CustomView/FocusedTextField.swift b/SodaLive/Sources/CustomView/FocusedTextField.swift
index c8b7970..9602278 100644
--- a/SodaLive/Sources/CustomView/FocusedTextField.swift
+++ b/SodaLive/Sources/CustomView/FocusedTextField.swift
@@ -37,7 +37,7 @@ struct FocusedTextField: UIViewRepresentable {
         textField.autocapitalizationType = .none
         textField.autocorrectionType = .no
         
-        let placeholder = "채널명을 입력해 보세요"
+        let placeholder = "검색"
         textField.placeholder = placeholder
         textField.attributedPlaceholder = NSAttributedString(
             string: placeholder,
diff --git a/SodaLive/Sources/Explorer/Profile/UserProfileView.swift b/SodaLive/Sources/Explorer/Profile/UserProfileView.swift
index 0b0f84d..6afbb6f 100644
--- a/SodaLive/Sources/Explorer/Profile/UserProfileView.swift
+++ b/SodaLive/Sources/Explorer/Profile/UserProfileView.swift
@@ -10,6 +10,8 @@ import SwiftUI
 struct UserProfileView: View {
     
     let userId: Int
+    
+    @Environment(\.presentationMode) var presentationMode: Binding<PresentationMode>
     @StateObject var viewModel = UserProfileViewModel()
     
     @State private var memberId: Int = 0
@@ -24,7 +26,11 @@ struct UserProfileView: View {
                 VStack(spacing: 0) {
                     HStack(spacing: 0) {
                         Button {
-                            AppState.shared.back()
+                            if presentationMode.wrappedValue.isPresented {
+                                presentationMode.wrappedValue.dismiss()
+                            } else {
+                                AppState.shared.back()
+                            }
                         } label: {
                             Image("ic_back")
                                 .resizable()
@@ -250,6 +256,8 @@ struct UserProfileView: View {
                         }
                     }
                 }
+                .navigationTitle("")
+                .navigationBarBackButtonHidden()
                 .popup(isPresented: $viewModel.isShowPopup, type: .toast, position: .bottom, autohideIn: 2) {
                     HStack {
                         Spacer()
diff --git a/SodaLive/Sources/Search/SearchApi.swift b/SodaLive/Sources/Search/SearchApi.swift
new file mode 100644
index 0000000..d2e4d98
--- /dev/null
+++ b/SodaLive/Sources/Search/SearchApi.swift
@@ -0,0 +1,92 @@
+//
+//  SearchApi.swift
+//  SodaLive
+//
+//  Created by klaus on 3/27/25.
+//
+
+import Foundation
+import Moya
+
+enum SearchApi {
+    case searchUnified(keyword: String, isAdultContentVisible: Bool, contentType: ContentType)
+    case searchCreatorList(keyword: String, isAdultContentVisible: Bool, contentType: ContentType, page: Int, size: Int)
+    case searchContentList(keyword: String, isAdultContentVisible: Bool, contentType: ContentType, page: Int, size: Int)
+    case searchSeriesList(keyword: String, isAdultContentVisible: Bool, contentType: ContentType, page: Int, size: Int)
+}
+
+extension SearchApi: TargetType {
+    var baseURL: URL {
+        return URL(string: BASE_URL)!
+    }
+    
+    var path: String {
+        switch self {
+        case .searchUnified:
+            return "/search"
+            
+        case .searchCreatorList:
+            return "/search/creators"
+            
+        case .searchContentList:
+            return "/search/contents"
+            
+        case .searchSeriesList:
+            return "/search/series"
+        }
+    }
+    
+    var method: Moya.Method {
+        return .get
+    }
+    
+    var task: Moya.Task {
+        switch self {
+        case .searchUnified(let keyword, let isAdultContentVisible, let contentType):
+            let parameters = [
+                "keyword": keyword,
+                "isAdultContentVisible": isAdultContentVisible,
+                "contentType": contentType
+            ] as [String : Any]
+            
+            return .requestParameters(parameters: parameters, encoding: URLEncoding.queryString)
+            
+        case .searchCreatorList(let keyword, let isAdultContentVisible, let contentType, let page, let size):
+            let parameters = [
+                "keyword": keyword,
+                "isAdultContentVisible": isAdultContentVisible,
+                "contentType": contentType,
+                "page": page - 1,
+                "size": size
+            ] as [String : Any]
+            
+            return .requestParameters(parameters: parameters, encoding: URLEncoding.queryString)
+            
+        case .searchContentList(let keyword, let isAdultContentVisible, let contentType, let page, let size):
+            let parameters = [
+                "keyword": keyword,
+                "isAdultContentVisible": isAdultContentVisible,
+                "contentType": contentType,
+                "page": page - 1,
+                "size": size
+            ] as [String : Any]
+            
+            return .requestParameters(parameters: parameters, encoding: URLEncoding.queryString)
+            
+        case .searchSeriesList(let keyword, let isAdultContentVisible, let contentType, let page, let size):
+            let parameters = [
+                "keyword": keyword,
+                "isAdultContentVisible": isAdultContentVisible,
+                "contentType": contentType,
+                "page": page - 1,
+                "size": size
+            ] as [String : Any]
+            
+            return .requestParameters(parameters: parameters, encoding: URLEncoding.queryString)
+        }
+    }
+    
+    var headers: [String : String]? {
+        return ["Authorization": "Bearer \(UserDefaults.string(forKey: UserDefaultsKey.token))"]
+    }
+}
diff --git a/SodaLive/Sources/Search/SearchContentListView.swift b/SodaLive/Sources/Search/SearchContentListView.swift
new file mode 100644
index 0000000..d5d6e39
--- /dev/null
+++ b/SodaLive/Sources/Search/SearchContentListView.swift
@@ -0,0 +1,45 @@
+//
+//  SearchContentListView.swift
+//  SodaLive
+//
+//  Created by klaus on 3/27/25.
+//
+
+import SwiftUI
+
+struct SearchContentListView: View {
+    
+    let itemsList: [SearchResponseItem]
+    
+    var body: some View {
+        ScrollView(.vertical, showsIndicators: false) {
+            VStack(spacing: 13.3) {
+                ForEach(0..<itemsList.count, id: \.self) {
+                    SearchContentItemView(item: itemsList[$0])
+                }
+            }
+            .padding(.horizontal, 13.3)
+        }
+    }
+}
+
+#Preview {
+    SearchContentListView(
+        itemsList: [
+            SearchResponseItem(
+                id: 1,
+                imageUrl: "https://test-cf.sodalive.net/profile/default-profile.png",
+                title: "Title1",
+                nickname: "Tester1",
+                type: .CONTENT
+            ),
+            SearchResponseItem(
+                id: 2,
+                imageUrl: "https://test-cf.sodalive.net/profile/default-profile.png",
+                title: "Title2",
+                nickname: "Tester2",
+                type: .CONTENT
+            )
+        ]
+    )
+}
diff --git a/SodaLive/Sources/Search/SearchCreatorListView.swift b/SodaLive/Sources/Search/SearchCreatorListView.swift
new file mode 100644
index 0000000..817171d
--- /dev/null
+++ b/SodaLive/Sources/Search/SearchCreatorListView.swift
@@ -0,0 +1,45 @@
+//
+//  SearchCreatorListView.swift
+//  SodaLive
+//
+//  Created by klaus on 3/27/25.
+//
+
+import SwiftUI
+
+struct SearchCreatorListView: View {
+    
+    let itemsList: [SearchResponseItem]
+    
+    var body: some View {
+        ScrollView(.vertical, showsIndicators: false) {
+            VStack(spacing: 13.3) {
+                ForEach(0..<itemsList.count, id: \.self) {
+                    SearchCreatorItemView(item: itemsList[$0])
+                }
+            }
+            .padding(.horizontal, 13.3)
+        }
+    }
+}
+
+#Preview {
+    SearchCreatorListView(
+        itemsList: [
+            SearchResponseItem(
+                id: 1,
+                imageUrl: "https://test-cf.sodalive.net/profile/default-profile.png",
+                title: "Tester",
+                nickname: "Tester",
+                type: .CREATOR
+            ),
+            SearchResponseItem(
+                id: 2,
+                imageUrl: "https://test-cf.sodalive.net/profile/default-profile.png",
+                title: "Tester2",
+                nickname: "Tester2",
+                type: .CREATOR
+            )
+        ]
+    )
+}
diff --git a/SodaLive/Sources/Search/SearchRepository.swift b/SodaLive/Sources/Search/SearchRepository.swift
new file mode 100644
index 0000000..24eb17b
--- /dev/null
+++ b/SodaLive/Sources/Search/SearchRepository.swift
@@ -0,0 +1,61 @@
+//
+//  SearchRepository.swift
+//  SodaLive
+//
+//  Created by klaus on 3/27/25.
+//
+
+import Foundation
+import CombineMoya
+import Combine
+import Moya
+
+final class SearchRepository {
+    private let api = MoyaProvider<SearchApi>()
+    
+    func searchUnified(keyword: String) -> AnyPublisher<Response, MoyaError> {
+        return api.requestPublisher(
+            .searchUnified(
+                keyword: keyword,
+                isAdultContentVisible: UserDefaults.isAdultContentVisible(),
+                contentType: ContentType(rawValue: UserDefaults.string(forKey: .contentPreference)) ?? ContentType.ALL
+            )
+        )
+    }
+    
+    func searchCreatorList(keyword: String, page: Int, size: Int) -> AnyPublisher<Response, MoyaError> {
+        return api.requestPublisher(
+            .searchCreatorList(
+                keyword: keyword,
+                isAdultContentVisible: UserDefaults.isAdultContentVisible(),
+                contentType: ContentType(rawValue: UserDefaults.string(forKey: .contentPreference)) ?? ContentType.ALL,
+                page: page,
+                size: size
+            )
+        )
+    }
+    
+    func searchContentList(keyword: String, page: Int, size: Int) -> AnyPublisher<Response, MoyaError> {
+        return api.requestPublisher(
+            .searchContentList(
+                keyword: keyword,
+                isAdultContentVisible: UserDefaults.isAdultContentVisible(),
+                contentType: ContentType(rawValue: UserDefaults.string(forKey: .contentPreference)) ?? ContentType.ALL,
+                page: page,
+                size: size
+            )
+        )
+    }
+    
+    func searchSeriesList(keyword: String, page: Int, size: Int) -> AnyPublisher<Response, MoyaError> {
+        return api.requestPublisher(
+            .searchSeriesList(
+                keyword: keyword,
+                isAdultContentVisible: UserDefaults.isAdultContentVisible(),
+                contentType: ContentType(rawValue: UserDefaults.string(forKey: .contentPreference)) ?? ContentType.ALL,
+                page: page,
+                size: size
+            )
+        )
+    }
+}
diff --git a/SodaLive/Sources/Search/SearchResponse.swift b/SodaLive/Sources/Search/SearchResponse.swift
new file mode 100644
index 0000000..e110fb8
--- /dev/null
+++ b/SodaLive/Sources/Search/SearchResponse.swift
@@ -0,0 +1,29 @@
+//
+//  SearchResponse.swift
+//  SodaLive
+//
+//  Created by klaus on 3/27/25.
+//
+
+struct SearchUnifiedResponse: Decodable {
+    let creatorList: [SearchResponseItem]
+    let contentList: [SearchResponseItem]
+    let seriesList: [SearchResponseItem]
+}
+
+struct SearchResponse: Decodable {
+    let totalCount: Int
+    let items: [SearchResponseItem]
+}
+
+struct SearchResponseItem: Decodable {
+    let id: Int
+    let imageUrl: String
+    let title: String
+    let nickname: String
+    let type: SearchResponseType
+}
+
+enum SearchResponseType: String, Decodable {
+    case CREATOR, CONTENT, SERIES
+}
diff --git a/SodaLive/Sources/Search/SearchSeriesListView.swift b/SodaLive/Sources/Search/SearchSeriesListView.swift
new file mode 100644
index 0000000..da30c01
--- /dev/null
+++ b/SodaLive/Sources/Search/SearchSeriesListView.swift
@@ -0,0 +1,44 @@
+//
+//  SearchSeriesListView.swift
+//  SodaLive
+//
+//  Created by klaus on 3/27/25.
+//
+
+import SwiftUI
+
+struct SearchSeriesListView: View {
+    
+    let itemsList: [SearchResponseItem]
+    
+    var body: some View {
+        ScrollView(.vertical, showsIndicators: false) {
+            VStack(spacing: 13.3) {
+                ForEach(0..<itemsList.count, id: \.self) {
+                    SearchSeriesItemView(item: itemsList[$0])
+                }
+            }
+            .padding(.horizontal, 13.3)
+        }
+    }
+}
+
+#Preview {
+    SearchSeriesListView(
+        itemsList: [
+            SearchResponseItem(
+                id: 1,
+                imageUrl: "https://test-cf.sodalive.net/profile/default-profile.png",
+                title: "Title1",
+                nickname: "Tester1",
+                type: .SERIES
+            ),
+            SearchResponseItem(
+                id: 2,
+                imageUrl: "https://test-cf.sodalive.net/profile/default-profile.png",
+                title: "Title2",
+                nickname: "Tester2",
+                type: .SERIES
+            )
+        ])
+}
diff --git a/SodaLive/Sources/Search/SearchUnifiedView.swift b/SodaLive/Sources/Search/SearchUnifiedView.swift
new file mode 100644
index 0000000..2c3b4ec
--- /dev/null
+++ b/SodaLive/Sources/Search/SearchUnifiedView.swift
@@ -0,0 +1,239 @@
+//
+//  SearchUnifiedView.swift
+//  SodaLive
+//
+//  Created by klaus on 3/27/25.
+//
+
+import SwiftUI
+import Kingfisher
+
+struct SearchUnifiedView: View {
+    
+    let creatorList: [SearchResponseItem]
+    let contentList: [SearchResponseItem]
+    let searchList: [SearchResponseItem]
+    
+    let onTapMoreCreator: () -> Void
+    let onTapMoreContent: () -> Void
+    let onTapMoreSeries: () -> Void
+    
+    var body: some View {
+        ScrollView(.vertical, showsIndicators: false) {
+            VStack(spacing: 30) {
+                if !creatorList.isEmpty {
+                    SearchUnifiedItemView(
+                        title: "채널",
+                        itemList: creatorList,
+                        onTapMore: onTapMoreCreator
+                    )
+                    .frame(maxWidth: .infinity)
+                    .padding(.horizontal, 13.3)
+                }
+                
+                if !contentList.isEmpty {
+                    SearchUnifiedItemView(
+                        title: "콘텐츠",
+                        itemList: contentList,
+                        onTapMore: onTapMoreContent
+                    )
+                    .frame(maxWidth: .infinity)
+                    .padding(.horizontal, 13.3)
+                }
+                
+                if !searchList.isEmpty {
+                    SearchUnifiedItemView(
+                        title: "시리즈",
+                        itemList: searchList,
+                        onTapMore: onTapMoreSeries
+                    )
+                    .frame(maxWidth: .infinity)
+                    .padding(.horizontal, 13.3)
+                }
+            }
+        }
+    }
+}
+
+struct SearchUnifiedItemView: View {
+    let title: String
+    let itemList: [SearchResponseItem]
+    let onTapMore: () -> Void
+    
+    var body: some View {
+        VStack(alignment: .leading, spacing: 13.3) {
+            Text(title)
+                .font(.custom(Font.bold.rawValue, size: 16))
+                .foregroundColor(.grayee)
+            
+            ForEach(0..<itemList.count, id: \.self) {
+                let item = itemList[$0]
+                
+                switch item.type {
+                case .CREATOR:
+                    SearchCreatorItemView(item: item)
+                    
+                case .CONTENT:
+                    SearchContentItemView(item: item)
+                    
+                case .SERIES:
+                    SearchSeriesItemView(item: item)
+                }
+            }
+            
+            Text("더보기 >")
+                .font(.custom(Font.medium.rawValue, size: 13.3))
+                .foregroundColor(.grayee)
+                .padding(.vertical, 10)
+                .frame(maxWidth: .infinity)
+                .background(Color.gray33.opacity(0.7))
+                .onTapGesture { onTapMore() }
+        }
+    }
+}
+
+struct SearchCreatorItemView: View {
+    let item: SearchResponseItem
+    
+    var body: some View {
+        NavigationLink {
+            UserProfileView(userId: item.id)
+        } label: {
+            HStack(spacing: 13.3) {
+                KFImage(URL(string: item.imageUrl))
+                    .cancelOnDisappear(true)
+                    .downsampling(size: CGSize(width: 60, height: 60))
+                    .resizable()
+                    .frame(width: 60, height: 60)
+                    .clipShape(Circle())
+                
+                Text(item.nickname)
+                    .font(.custom(Font.medium.rawValue, size: 13.3))
+                    .foregroundColor(Color.grayee)
+                
+                Spacer()
+            }
+            .frame(maxWidth: .infinity)
+            .contentShape(Rectangle())
+        }
+    }
+}
+
+struct SearchContentItemView: View {
+    let item: SearchResponseItem
+    
+    var body: some View {
+        NavigationLink {
+            ContentDetailView(contentId: item.id)
+        } label: {
+            HStack(spacing: 13.3) {
+                KFImage(URL(string: item.imageUrl))
+                    .cancelOnDisappear(true)
+                    .downsampling(size: CGSize(width: 60, height: 60))
+                    .resizable()
+                    .frame(width: 60, height: 60)
+                    .cornerRadius(5.3)
+                
+                VStack(alignment: .leading, spacing: 6.7) {
+                    Text(item.title)
+                        .font(.custom(Font.medium.rawValue, size: 13.3))
+                        .foregroundColor(Color.grayee)
+                    
+                    Text(item.nickname)
+                        .font(.custom(Font.medium.rawValue, size: 10))
+                        .foregroundColor(Color.gray77)
+                }
+                
+                Spacer()
+            }
+            .frame(maxWidth: .infinity)
+            .contentShape(Rectangle())
+        }
+    }
+}
+
+struct SearchSeriesItemView: View {
+    let item: SearchResponseItem
+    
+    var body: some View {
+        NavigationLink {
+            SeriesDetailView(seriesId: item.id)
+        } label: {
+            HStack(spacing: 13.3) {
+                KFImage(URL(string: item.imageUrl))
+                    .cancelOnDisappear(true)
+                    .downsampling(size: CGSize(width: 60, height: 85))
+                    .resizable()
+                    .scaledToFill()
+                    .frame(width: 60, height: 85)
+                    .clipped()
+                    .cornerRadius(5.3)
+                
+                VStack(alignment: .leading, spacing: 6.7) {
+                    Text(item.title)
+                        .font(.custom(Font.medium.rawValue, size: 13.3))
+                        .foregroundColor(Color.grayee)
+                    
+                    Text(item.nickname)
+                        .font(.custom(Font.medium.rawValue, size: 10))
+                        .foregroundColor(Color.gray77)
+                }
+                
+                Spacer()
+            }
+            .frame(maxWidth: .infinity)
+            .contentShape(Rectangle())
+        }
+    }
+}
+
+
+#Preview {
+    SearchUnifiedView(
+        creatorList: [
+            SearchResponseItem(
+                id: 1,
+                imageUrl: "https://test-cf.sodalive.net/profile/default-profile.png",
+                title: "Tester",
+                nickname: "Tester",
+                type: .CREATOR
+            )
+        ],
+        contentList: [
+            SearchResponseItem(
+                id: 1,
+                imageUrl: "https://test-cf.sodalive.net/profile/default-profile.png",
+                title: "Title1",
+                nickname: "Tester",
+                type: .CONTENT
+            ),
+            
+            SearchResponseItem(
+                id: 2,
+                imageUrl: "https://test-cf.sodalive.net/profile/default-profile.png",
+                title: "Title2",
+                nickname: "Tester2",
+                type: .CONTENT
+            )
+        ],
+        searchList: [
+            SearchResponseItem(
+                id: 1,
+                imageUrl: "https://test-cf.sodalive.net/profile/default-profile.png",
+                title: "Title1",
+                nickname: "Tester",
+                type: .SERIES
+            ),
+            
+            SearchResponseItem(
+                id: 2,
+                imageUrl: "https://test-cf.sodalive.net/profile/default-profile.png",
+                title: "Title2",
+                nickname: "Tester2",
+                type: .SERIES
+            )],
+        onTapMoreCreator: {},
+        onTapMoreContent: {},
+        onTapMoreSeries: {}
+    )
+}
diff --git a/SodaLive/Sources/Search/SearchView.swift b/SodaLive/Sources/Search/SearchView.swift
new file mode 100644
index 0000000..46f99b5
--- /dev/null
+++ b/SodaLive/Sources/Search/SearchView.swift
@@ -0,0 +1,165 @@
+//
+//  SearchView.swift
+//  SodaLive
+//
+//  Created by klaus on 3/27/25.
+//
+
+import SwiftUI
+import Kingfisher
+
+struct SearchViewTabItem {
+    let title: String
+    let tab: SearchViewModel.CurrentTab
+}
+
+struct SearchView: View {
+    
+    @StateObject var viewModel = SearchViewModel()
+    @State private var isFocused: Bool = false
+    
+    let tabItemList = [
+        SearchViewTabItem(title: "통합", tab: .UNIFIED),
+        SearchViewTabItem(title: "채널", tab: .CREATOR),
+        SearchViewTabItem(title: "콘텐츠", tab: .CONTENT),
+        SearchViewTabItem(title: "시리즈", tab: .SERIES)
+    ]
+    
+    var body: some View {
+        NavigationView {
+            BaseView(isLoading: $viewModel.isLoading) {
+                VStack(spacing: 0) {
+                    HStack(spacing: 0) {
+                        Button {
+                            AppState.shared.back()
+                        } label: {
+                            Image("ic_back")
+                                .resizable()
+                                .frame(width: 20, height: 20)
+                        }
+                        .padding(13.3)
+                        
+                        HStack(spacing: 0) {
+                            Image("ic_title_search_black")
+                            
+                            FocusedTextField(
+                                text: $viewModel.keyword,
+                                isFirstResponder: isFocused
+                            )
+                            .padding(.horizontal, 13.3)
+                            .onAppear {
+                                DispatchQueue.main.asyncAfter(deadline: .now() + 0.2) {
+                                    isFocused = true
+                                }
+                            }
+                        }
+                        .padding(.horizontal, 21.3)
+                        .frame(height: 50)
+                        .frame(maxWidth: .infinity)
+                        .background(Color.gray22)
+                        .overlay(
+                            RoundedRectangle(cornerRadius: 6.7)
+                                .strokeBorder(lineWidth: 1)
+                                .foregroundColor(Color.graybb)
+                        )
+                    }
+                    .padding(.trailing, 13.3)
+                    
+                    if !viewModel.searchUnifiedCreatorList.isEmpty ||
+                        !viewModel.searchUnifiedContentList.isEmpty ||
+                        !viewModel.searchUnifiedSeriesList.isEmpty {
+                        ScrollView(.horizontal, showsIndicators: false) {
+                            HStack(spacing: 8) {
+                                ForEach(0..<tabItemList.count, id: \.self) { index in
+                                    let tabItem = tabItemList[index]
+                                    
+                                    Text(tabItem.title)
+                                        .font(.custom(Font.medium.rawValue,size: 16))
+                                        .foregroundColor(
+                                            viewModel.currentTab == tabItem.tab ?
+                                                .button :
+                                                    .graybb
+                                        )
+                                        .padding(.horizontal, 12)
+                                        .onTapGesture {
+                                            if viewModel.currentTab != tabItem.tab {
+                                                viewModel.currentTab = tabItem.tab
+                                            }
+                                        }
+                                }
+                            }
+                            .padding(.vertical, 15)
+                            .padding(.horizontal, 13.3)
+                        }
+                        .padding(.bottom, 13.3)
+                        
+                        switch viewModel.currentTab {
+                        case .UNIFIED:
+                            SearchUnifiedView(
+                                creatorList: viewModel.searchUnifiedCreatorList,
+                                contentList: viewModel.searchUnifiedContentList,
+                                searchList: viewModel.searchUnifiedSeriesList,
+                                onTapMoreCreator: {
+                                    viewModel.currentTab = .CREATOR
+                                },
+                                onTapMoreContent: {
+                                    viewModel.currentTab = .CONTENT
+                                },
+                                onTapMoreSeries: {
+                                    viewModel.currentTab = .SERIES
+                                }
+                            )
+                            
+                        case .CREATOR:
+                            SearchCreatorListView(itemsList: viewModel.searchCreatorItemList)
+                            
+                        case .CONTENT:
+                            SearchContentListView(itemsList: viewModel.searchContentItemList)
+                            
+                        case .SERIES:
+                            SearchSeriesListView(itemsList: viewModel.searchSeriesItemList)
+                        }
+                    }
+                    
+                    if viewModel.searchUnifiedCreatorList.isEmpty &&
+                        viewModel.searchUnifiedContentList.isEmpty &&
+                        viewModel.searchUnifiedSeriesList.isEmpty &&
+                        viewModel.keyword.count > 2 {
+                        Text("검색 결과가 없습니다.")
+                            .font(.custom(Font.medium.rawValue, size: 18.3))
+                            .foregroundColor(.white)
+                            .padding(.top, 20)
+                    }
+                    
+                    Spacer()
+                }
+                .popup(isPresented: $viewModel.isShowPopup, type: .toast, position: .top, autohideIn: 2) {
+                    GeometryReader { geo in
+                        HStack {
+                            Spacer()
+                            Text(viewModel.errorMessage)
+                                .padding(.vertical, 13.3)
+                                .frame(width: geo.size.width - 66.7, alignment: .center)
+                                .font(.custom(Font.medium.rawValue, size: 12))
+                                .background(Color.button)
+                                .foregroundColor(Color.white)
+                                .multilineTextAlignment(.center)
+                                .cornerRadius(20)
+                                .padding(.top, 66.7)
+                            Spacer()
+                        }
+                    }
+                }
+                .onAppear {
+                    if viewModel.keyword.isEmpty {
+                        viewModel.keyword = UserDefaults.string(forKey: .searchChannel)
+                    }
+                }
+            }
+        }
+    }
+}
+
+#Preview {
+    SearchView()
+}
diff --git a/SodaLive/Sources/Search/SearchViewModel.swift b/SodaLive/Sources/Search/SearchViewModel.swift
new file mode 100644
index 0000000..fd71b01
--- /dev/null
+++ b/SodaLive/Sources/Search/SearchViewModel.swift
@@ -0,0 +1,278 @@
+//
+//  SearchViewModel.swift
+//  SodaLive
+//
+//  Created by klaus on 3/27/25.
+//
+
+import Foundation
+import Combine
+
+final class SearchViewModel: ObservableObject {
+    enum CurrentTab: String {
+        case UNIFIED, CREATOR, CONTENT, SERIES
+    }
+    
+    private let repository = SearchRepository()
+    private var subscription = Set<AnyCancellable>()
+    
+    @Published var currentTab: CurrentTab = .UNIFIED {
+        didSet {
+            if currentTab == .CREATOR && searchCreatorItemList.isEmpty {
+                self.searchCreatorList()
+            } else if currentTab == .CONTENT && searchContentItemList.isEmpty {
+                self.searchContentList()
+            } else if currentTab == .SERIES && searchSeriesItemList.isEmpty {
+                self.searchSeriesList()
+            }
+        }
+    }
+    
+    @Published var errorMessage = ""
+    @Published var isShowPopup = false
+    @Published var isLoading = false
+    
+    @Published var keyword = ""
+    
+    @Published var searchUnifiedCreatorList: [SearchResponseItem] = []
+    @Published var searchUnifiedContentList: [SearchResponseItem] = []
+    @Published var searchUnifiedSeriesList: [SearchResponseItem] = []
+    
+    @Published var searchCreatorItemList: [SearchResponseItem] = []
+    @Published var searchContentItemList: [SearchResponseItem] = []
+    @Published var searchSeriesItemList: [SearchResponseItem] = []
+    
+    var searchCreatorPage = 1
+    var searchContentPage = 1
+    var searchSeriesPage = 1
+    
+    var isSearchCreatorLast = false
+    var isSearchContentLast = false
+    var isSearchSeriesLast = false
+    
+    private var pageSize = 20
+    
+    init() {
+        _keyword = Published(initialValue: "")
+        $keyword
+            .debounce(for: .seconds(0.3), scheduler: RunLoop.main)
+            .sink { [unowned self] value in
+                UserDefaults.set(value, forKey: .searchChannel)
+                if value.count > 1 {
+                    self.searchUnified()
+                } else {
+                    self.initList()
+                }
+            }
+            .store(in: &subscription)
+    }
+    
+    func initList() {
+        searchCreatorPage = 1
+        searchContentPage = 1
+        searchSeriesPage = 1
+        
+        isSearchCreatorLast = false
+        isSearchContentLast = false
+        isSearchSeriesLast = false
+        
+        searchUnifiedCreatorList.removeAll()
+        searchUnifiedContentList.removeAll()
+        searchUnifiedSeriesList.removeAll()
+        
+        searchCreatorItemList.removeAll()
+        searchContentItemList.removeAll()
+        searchSeriesItemList.removeAll()
+    }
+    
+    func searchUnified() {
+        if !isLoading {
+            if currentTab != .UNIFIED {
+                currentTab = .UNIFIED
+            }
+            
+            initList()
+            
+            isLoading = true
+            
+            repository.searchUnified(keyword: keyword)
+                .sink { result in
+                    switch result {
+                    case .finished:
+                        DEBUG_LOG("finish")
+                    case .failure(let error):
+                        ERROR_LOG(error.localizedDescription)
+                    }
+                } receiveValue: { [unowned self] response in
+                    self.isLoading = false
+                    let responseData = response.data
+                    
+                    do {
+                        let jsonDecoder = JSONDecoder()
+                        let decoded = try jsonDecoder.decode(ApiResponse<SearchUnifiedResponse>.self, from: responseData)
+                        
+                        if let data = decoded.data, decoded.success {
+                            DEBUG_LOG("test: \(data)")
+                            searchUnifiedCreatorList.append(contentsOf: data.creatorList)
+                            searchUnifiedContentList.append(contentsOf: data.contentList)
+                            searchUnifiedSeriesList.append(contentsOf: data.seriesList)
+                        } else {
+                            if let message = decoded.message {
+                                self.errorMessage = message
+                            } else {
+                                self.errorMessage = "다시 시도해 주세요.\n계속 같은 문제가 발생할 경우 고객센터로 문의 주시기 바랍니다."
+                            }
+                            
+                            self.isShowPopup = true
+                        }
+                    } catch {
+                        DEBUG_LOG("error: \(error)")
+                        self.errorMessage = "다시 시도해 주세요.\n계속 같은 문제가 발생할 경우 고객센터로 문의 주시기 바랍니다."
+                        self.isShowPopup = true
+                    }
+                }
+                .store(in: &subscription)
+        }
+    }
+    
+    func searchCreatorList() {
+        if !isLoading && !isSearchCreatorLast {
+            isLoading = true
+            
+            repository.searchCreatorList(keyword: keyword, page: searchCreatorPage, size: pageSize)
+                .sink { result in
+                    switch result {
+                    case .finished:
+                        DEBUG_LOG("finish")
+                    case .failure(let error):
+                        ERROR_LOG(error.localizedDescription)
+                    }
+                } receiveValue: { [unowned self] response in
+                    self.isLoading = false
+                    let responseData = response.data
+                    
+                    do {
+                        let jsonDecoder = JSONDecoder()
+                        let decoded = try jsonDecoder.decode(ApiResponse<SearchResponse>.self, from: responseData)
+                        
+                        if let data = decoded.data, decoded.success {
+                            DEBUG_LOG("test: \(data)")
+                            self.searchCreatorPage += 1
+                            self.searchCreatorItemList.append(contentsOf: data.items)
+                            
+                            if data.items.isEmpty {
+                                self.isSearchCreatorLast = true
+                            }
+                        } else {
+                            if let message = decoded.message {
+                                self.errorMessage = message
+                            } else {
+                                self.errorMessage = "다시 시도해 주세요.\n계속 같은 문제가 발생할 경우 고객센터로 문의 주시기 바랍니다."
+                            }
+                            
+                            self.isShowPopup = true
+                        }
+                    } catch {
+                        ERROR_LOG("test: \(error)")
+                        self.errorMessage = "다시 시도해 주세요.\n계속 같은 문제가 발생할 경우 고객센터로 문의 주시기 바랍니다."
+                        self.isShowPopup = true
+                    }
+                }
+                .store(in: &subscription)
+        }
+    }
+    
+    func searchContentList() {
+        if !isLoading && !isSearchContentLast {
+            isLoading = true
+            
+            repository.searchContentList(keyword: keyword, page: searchContentPage, size: pageSize)
+                .sink { result in
+                    switch result {
+                    case .finished:
+                        DEBUG_LOG("finish")
+                    case .failure(let error):
+                        ERROR_LOG(error.localizedDescription)
+                    }
+                } receiveValue: { [unowned self] response in
+                    self.isLoading = false
+                    let responseData = response.data
+                    
+                    do {
+                        let jsonDecoder = JSONDecoder()
+                        let decoded = try jsonDecoder.decode(ApiResponse<SearchResponse>.self, from: responseData)
+                        
+                        if let data = decoded.data, decoded.success {
+                            DEBUG_LOG("test: \(data)")
+                            self.searchContentPage += 1
+                            self.searchContentItemList.append(contentsOf: data.items)
+                            
+                            if data.items.isEmpty {
+                                self.isSearchContentLast = true
+                            }
+                        } else {
+                            if let message = decoded.message {
+                                self.errorMessage = message
+                            } else {
+                                self.errorMessage = "다시 시도해 주세요.\n계속 같은 문제가 발생할 경우 고객센터로 문의 주시기 바랍니다."
+                            }
+                            
+                            self.isShowPopup = true
+                        }
+                    } catch {
+                        ERROR_LOG("test: \(error)")
+                        self.errorMessage = "다시 시도해 주세요.\n계속 같은 문제가 발생할 경우 고객센터로 문의 주시기 바랍니다."
+                        self.isShowPopup = true
+                    }
+                }
+                .store(in: &subscription)
+        }
+    }
+    
+    func searchSeriesList() {
+        if !isLoading && !isSearchSeriesLast {
+            isLoading = true
+            
+            repository.searchSeriesList(keyword: keyword, page: searchSeriesPage, size: pageSize)
+                .sink { result in
+                    switch result {
+                    case .finished:
+                        DEBUG_LOG("finish")
+                    case .failure(let error):
+                        ERROR_LOG(error.localizedDescription)
+                    }
+                } receiveValue: { [unowned self] response in
+                    self.isLoading = false
+                    let responseData = response.data
+                    
+                    do {
+                        let jsonDecoder = JSONDecoder()
+                        let decoded = try jsonDecoder.decode(ApiResponse<SearchResponse>.self, from: responseData)
+                        
+                        if let data = decoded.data, decoded.success {
+                            DEBUG_LOG("test; \(data)")
+                            self.searchSeriesPage += 1
+                            self.searchSeriesItemList.append(contentsOf: data.items)
+                            
+                            if data.items.isEmpty {
+                                self.isSearchSeriesLast = true
+                            }
+                        } else {
+                            if let message = decoded.message {
+                                self.errorMessage = message
+                            } else {
+                                self.errorMessage = "다시 시도해 주세요.\n계속 같은 문제가 발생할 경우 고객센터로 문의 주시기 바랍니다."
+                            }
+                            
+                            self.isShowPopup = true
+                        }
+                    } catch {
+                        ERROR_LOG("test: \(error)")
+                        self.errorMessage = "다시 시도해 주세요.\n계속 같은 문제가 발생할 경우 고객센터로 문의 주시기 바랍니다."
+                        self.isShowPopup = true
+                    }
+                }
+                .store(in: &subscription)
+        }
+    }
+}