You will be fine

<Productivity> Mac 메뉴바앱 일단 만들기 - 코드변환기

by BFine
반응형

가.  만들려고한 이유

 a. 매번 찾기 번거로운데!

  -  전회사에서 이름 <-> 코드 형태의 데이터가 있는데 이 코드값이 외우기에는 조금 길고 20~30개 정도 되어서 매번 이름에 대한 코드를 찾거나

       이 코드가 이름이 뭔지 매번 DB에서 찾아야 했다. (간혹 개발과 운영 값이 다른 경우도 있어서 귀찮게 망분리 환경에 들어가서 찾아야함..)  

 

 b. 가장 간편한 방법이 없을까?

  -  이름에 해당하는 코드값은 절대 변하지 않는 값이어서 파일에 저장하고 단순 불러오기만 하면 될 것 같았고 생각나는게 mac 메뉴바 앱이었다

  -  메뉴바 앱을 누르고 팝업창이 뜨고 이름이나 코드를 넣고 조회만 누르면 찾을 수 있게 만들면 손이 덜 갈것 같아서 요걸 목표로 정했다!

 

 c. Swift 1도 모르는데..

  -  mac 메뉴바 앱을 만들때 고민했던 부분이 swift나 xcode를 써본 적이 없어서 괜히 시간낭비하는거 아닌가 싶었다 그렇지만 혼자 쓸거고 이정도면

      swift는 몰라도 나에게는 if-else로 도배하는 방법이 있으니까 충분할것 같아서 시도해보았다..!

 

나.  Mac 메뉴바앱 만들기전 준비물

 a.  Xcode 설치

  -  이 앱을 처음 만들때 노트북이 m1이 아니었어서 그런지 앱스토어에서는 설치가 안되어서 아래에서 받았았다 https://developer.apple.com/xcode/

 

Xcode 14 - Apple Developer

Xcode 14 includes everything you need to develop, test, and distribute apps across all Apple platforms.

developer.apple.com

  - 그런데 위에서 받으려면 가입하고 했던것 같으니 m1 이면 앱스토어에서 설치하자!

 

 b. 유튜브 따라하기 

  -  swift에 1도 모르는 상태였고 처음부터 배우자니 시간도 오래걸릴 것 같고 볼만한 책도 없었던 것 같다. 수정하는것은 자신 있으니 유튜브 강의를

     따라 만들면서 수정해야지 라는 안일한 생각으로 시작하게 되었다 ㅎㅎ  https://www.youtube.com/watch?v=Fn4YZFFVq2E 

 

경고 : swift는 지금도 전혀 모르기 때문에 코드가 이상할 수 있습니다..! 

 

다.  Mac 메뉴바앱 만들기 (feat. Swift)

 a.  메뉴바 View

import Foundation
import SwiftUI

struct MenuBarView: View {
    @State var find = "조회"
    @State var fileTitle = "파일생성"
    @State var code : String = ""
    @State var content : String = ""
    @State var showingAlert : Bool = false;

    var body: some View {
        VStack{
            HStack{
                Text("코드 : ")
                TextField("",text: $code)
                    .textFieldStyle(RoundedBorderTextFieldStyle())
            }.padding(.horizontal)
                .padding(.bottom)

            HStack{
                Text("내용 : ")
                TextField("",text: $content)
                    .textFieldStyle(RoundedBorderTextFieldStyle())
            }.padding(.horizontal)
                .padding(.bottom)

            HStack{
                TabButton(title: $find, code: $code , content: $content ).keyboardShortcut(.defaultAction)
                TabButton(title: $fileTitle, code: $code , content: $content)
            }
            .padding(.horizontal)
            .padding(.top)
            .frame(alignment: .bottom)


        }.frame(width: 250, height: 200)
    }

    
 }

  -  메뉴바앱을 클릭했을때 나오는 view를 생성해보았다. 나중에 Cocoa class 랑 xib interface 생성하면 코드말고 그릴 수 있는 방법이 있는걸 알게 되었는데

      생각보다 내맘대로 되는게 없어서 만들지는 못했다..  

 

 b.  파일 읽기 기능 만들기 

   func getCSVDataKeyCode(path:String) -> Dictionary<String, String> {
        do {
            let content = try String(contentsOfFile: path)
            var dictionary: [String: String] = [:]
            content.components(
                separatedBy: "\n"
            ).forEach{
                let code = $0.components(separatedBy: ",")[0]
                let content = $0.components(separatedBy: ",")[1]
                dictionary[code.uppercased()] = content
            }
            return dictionary
        }
        catch {
            return [:]
        }
    }

    func getCSVDataKeyContent(path:String) -> Dictionary<String, String> {
        do {
            let content = try String(contentsOfFile: path)
            var dictionary: [String: String] = [:]
            content.components(
                separatedBy: "\n"
            ).forEach{
                let code = $0.components(separatedBy: ",")[0]
                let content = $0.components(separatedBy: ",")[1]
                dictionary[content.uppercased()] = code
            }
            return dictionary
        }
        catch {
            return [:]
        }
    }

  -  텍스트 파일에 코드, 내용 형태로 저장하려고 생각했고 데이터가 많지 않을테니 메모리에 올려두면 되겠다 생각했다. 그래서 요청이 들어오면 파일 전체를

       읽어서 ","로 짤라 Map에 저장하고 요청한 값을 찾으면 되겠다 싶어서 저렇게 만들었다. Swift에서 MapDictionary 인듯 하다.

  -  java만 써서 그런지 머리가 java로만 굴러가서 switft 의 문법이 좀 이상한것(?) 같다. 하다보면 될 것 같은 것도 막상 컴파일&런타임 오류를

      계속 뱉어서 간단한거 하는데도 오래걸려서 분노가 머리 끝까지 차올랐다.!  => 위의 중복코드가 두개가 생긴 이유.. 나중에 고쳐야겠다.

 

 c. 파일 생성 & 조회 기능 만들기

    
var userHomeCodeConveterDirectoryPath : String {
    let pw = getpwuid(getuid())
    let home = pw?.pointee.pw_dir
    let homePath = FileManager.default.string(withFileSystemRepresentation: home!, length: Int(strlen(home!)))
    return homePath+"/CodeReader"
}

### button action ###

do {
  try FileManager.default.createDirectory(atPath:userHomeCodeConveterDirectoryPath, withIntermediateDirectories: false, attributes: nil)
  FileManager.default.createFile(atPath:userHomeCodeConveterDirectoryPath + fileName,contents: nil)

} catch let e {
    print(e.localizedDescription)
}
self.title = "파일열기"
shell(userHomeCodeConveterDirectoryPath+fileName)
    
####################
    
@discardableResult
func shell(_ args: String...) -> Int32 {
    let task = Process()
    task.launchPath = "/usr/bin/open"
    task.arguments = args
    task.launch()
    task.waitUntilExit()
    return task.terminationStatus
}

  -  처음에는 직접 디렉토리 생성하고 파일도 직접 만들었는데 버튼 누르면 만들어 주는게 좋을것 같아서 추가로 만든 부분이다. 

  -  디렉토리를 만들고 파일을 생성하도록 했다. 아래 shell 함수는 파일 생성이나 조회하면 실행시키고 싶어서 열심히 가장 깔끔한 방법으로 하려고

      구글링 했는데 나에게 최선의 방법이 저렇게 shell 명령 하는 것이 었다.. 이거 하는데도 꽤 걸렸었다 ㅎㅎ (좋은건지는 모름..)

       => 파일생성버튼을 누르면  ~/CodeReader/codelist.txt 가 생기는 형태이다!

  -  파일생성 이후에는 파일조회로 이름을 바꾸고 파일 조회 할 수 있도록 만들었다! (해당 코드는 길어져서 생략했다.) 

 

 d.  mac 메뉴바에서 보여주기 (+ 프로그램 종료 추가) 

@main
struct CodeReaderApp: App {
    @NSApplicationDelegateAdaptor(AppDelegate.self) var delegate
    class AppDelegate : NSObject, NSApplicationDelegate{
        
        var statusItem: NSStatusItem?
        var popOver = NSPopover()
        let menu = NSMenu()
        
        
        func applicationDidFinishLaunching(_ notification: Notification) {
            let exitMenuItem = NSMenuItem(title: "프로그램 종료", action: #selector(exitProgram(_:)), keyEquivalent: "q")
            menu.addItem(exitMenuItem)
                
            let menuView  = MenuBarView()
            
            popOver.behavior = .transient
            popOver.animates = true
            popOver.contentViewController = NSViewController()
            popOver.contentViewController?.view = NSHostingView(rootView: menuView)
            popOver.contentViewController?.view.window?.makeKey()
            
            statusItem = NSStatusBar.system.statusItem(withLength: NSStatusItem.variableLength)
            
            if let menuButton = statusItem?.button{
                menuButton.image = NSImage(systemSymbolName: "car.ferry", accessibilityDescription: nil)
                menuButton.action = #selector(menuButtonToggle)
                menuButton.sendAction(on: [.leftMouseUp, .rightMouseUp])
                menuButton.menu = menu
            }
            
        }
        
        
        @objc func menuButtonToggle(sender: AnyObject){
            
            if NSApp.currentEvent?.type == NSEvent.EventType.rightMouseUp {
                if let menuButton = statusItem?.button {
                    let location = NSPoint(x: menuButton.frame.width/30, y: -2)
                    let event = NSEvent.mouseEvent(with: NSEvent.EventType.rightMouseUp, location: location, modifierFlags: [], timestamp: 0, windowNumber: menuButton.window!.windowNumber, context: nil, eventNumber: 0, clickCount: 1, pressure: 1)
                    NSMenu.popUpContextMenu(menu, with: event!, for: menuButton)
                }
           } else {
               if popOver.isShown{
                   popOver.performClose(sender)
               }else{
                   if let menuButton = statusItem?.button{
                       self.popOver.show(relativeTo: menuButton.bounds, of: menuButton, preferredEdge: NSRectEdge.minY)
                   }
               }
           }
        }
        
        @objc func exitProgram(_ sender: Any?) {
            NSApplication.shared.terminate(sender)
        }
        
    }


    var body: some Scene {
        WindowGroup {
           ContentView()
        }
    }
}

  -  이렇게 보니 코드가 지저분하지만 원했던 동작을 할수있게 만들었다.!

           1. 마우스 왼쪽 버튼은 위에 만든 메뉴바 화면 노출  

           2. 마우스 오른쪽 버튼은 프로그램 종료 메뉴 노출

  -  이거 만드는데 1시간도 안걸릴줄 알았는데 방법 찾느라 며칠 걸렸다.. (구글링해도 잘안나온다.. swift하는 사람이 많이 없나?)

  -  메뉴바화면 노출 자체는 유튜브에 있어서 별로 걸리지 않았는데 프로그램종료 메뉴 만드는거는 온갖 삽질을 하다가 허무하게 저런 간단한 방법이 있었다..

      => (처음에 1번만 만들때 당시에는 chatgpt가 없었는데 2번 만들때는 상당히 도움을 많이 받았다. chatgpt로 다 만들면 1시간이면 만드려나...) 

  -  맨아래에 ContentView 같은 경우는 메뉴바 외에 일반화면이 나온다 이거는 제거하고 싶었는데 도저히 방법을 모르겠어서 사용법화면으로 쓰고있다..

 

라.  실행 테스트

 a.  CodeReader (메뉴바앱 이름)

  -  만든 앱에 이름은 CodeReader 이고 있어보이게 하기 위해 요즘 AI 디자인툴 이란게 있는데 글로 쓰면 그림그려주길래 로고로 썼다 ㅎㅎ

     => 오른쪽에 크게보면 좀 로고이미지가 없어보이긴 한 것 같다.. ㅎㅎ

 

 b.  실행영상

확대!

 

 c.  느낀점

  -  예전에 앱개발 공부할때나 간단한 프론트 수정할때 느끼는 부분이지만 참 화면 만드는 부분은 정말 내맘대로 되는게 없는것 같다 (진짜 혈압오른다)

  -  볼품없는 메뉴바앱이지만 막상 만들어서 사용하니까 은근 좀 뿌듯한게 있다 ㅎㅎ

      => 아쉽게도 요즘은 zoom 회의 비밀번호 검색용으로 쓰고있다 (주기적인 회의인데 비밀번호 걸려있는 것들은 외워지지가 않는다)

      => 그마저도 zoom 설정에 링크에 비밀번호 포함 기능이 사용법이 널리 퍼지고 있어 설자리를 잃어가고 있다..

  -  요즘 또 있으면 좋을것 같은거는 메뉴바앱을 클릭하면 인텔리제이 or 데이터그립 화면을 바로 보여주는게 있으면 좋을것 같다.

      => 프로그램여러게 띄워두면 찾는데 은근 피로감이 들어서 있으면 좋지 않을까 싶다!

반응형

블로그의 정보

57개월 BackEnd

BFine

활동하기