Golang으로 Neovim 설정하는 방법

Golang으로 Neovim 설정하는 방법
It SharingPosted On Jul 23, 20248 min read

요즘 Go 쓰기에 관심이 생겨 Neovim 설정에서 Golang을 설정해야 했어요. 여기에 어떻게 설정했는지 알려드릴게요.

이미지

저는 유지 및 계속 업데이트하는 개인 Neovim 설정이 있어요. 이 기사에서는 해당 설정에서 사용하는 플러그인을 이용할 거에요. 제 설정은 여기에서 확인할 수 있어요.

Neovim이나 LSP 설정에 익숙하지 않은 경우, 몇 가지 개념에 익숙해지기 위해 이전 기사 몇 편을 읽어보시는 걸 추천합니다.

일반적으로, 플러그인 설정에는 lazy.nvim을 사용하는 것이 좋습니다.

새 언어를 위해 Neovim을 구성하는 여러 측면이 있습니다.

  • LSP 구성하기 (언어 서버 추가 및 연결)
  • Treesitter 지원 추가 (문법 강조 및 다른 기능)
  • 서식 지정 구성 (자동 저장 포함)
  • 디버거 추가 및 구성

LSP에 추가

자, 언어 서버를 설치하고 설정해 봅시다.

gopls 언어 서버 설치

저는 새로운 언어로 작업할 때 필요한 다양한 종속성을 설치하고 관리하는 mason.nvim을 사용합니다.

{
    "williamboman/mason.nvim",
    dependencies = {
      "WhoIsSethDaniel/mason-tool-installer.nvim",
    },
    config = function()
      local mason = require("mason")
      local mason_tool_installer = require("mason-tool-installer")

      mason_tool_installer.setup({
        ensure_installed = {
          "gopls",
        },
      })
    end,
}

lsp-zero 설치 및 구성

나는 lsp zero를 사용하여 언어 서버를 내 Neovim 인스턴스에 연결합니다.

이 배포는 mason-lspconfig 및 nvim-lspconfig을 서로 통신하도록 설정하고 완성을 위해 nvim-cmp 및 스니펫 지원을 추가하는 등 다른 유용한 기능을 제공합니다.

내 키맵은 그대로 남겨 두었지만, 자유롭게 여러분에게 맞는 것으로 업데이트해 주세요.

{
  "VonHeikemen/lsp-zero.nvim",
  branch = "v2.x",
  dependencies = {
    -- LSP Support
    { "neovim/nvim-lspconfig" }, -- 필수
    { -- 선택적
      "williamboman/mason.nvim",
      build = function()
        pcall(vim.cmd, "MasonUpdate")
      end,
    },
    { "williamboman/mason-lspconfig.nvim" }, -- 선택적

    -- 자동 완성
    { "hrsh7th/nvim-cmp" }, -- 필수
    { "hrsh7th/cmp-nvim-lsp" }, -- 필수
    { "L3MON4D3/LuaSnip" }, -- 필수
    { "rafamadriz/friendly-snippets" },
    { "hrsh7th/cmp-buffer" },
    { "hrsh7th/cmp-path" },
    { "hrsh7th/cmp-cmdline" },
    { "saadparwaiz1/cmp_luasnip" },
  },
  config = function()
    local lsp = require("lsp-zero")

    lsp.on_attach(function(client, bufnr)
      local opts = { buffer = bufnr, remap = false }

      vim.keymap.set("n", "gr", function() vim.lsp.buf.references() end, vim.tbl_deep_extend("force", opts, { desc = "LSP 참조 이동" }))
      vim.keymap.set("n", "gd", function() vim.lsp.buf.definition() end, vim.tbl_deep_extend("force", opts, { desc = "LSP 정의 이동" }))
      vim.keymap.set("n", "K", function() vim.lsp.buf.hover() end, vim.tbl_deep_extend("force", opts, { desc = "LSP 툴팁" }))
      vim.keymap.set("n", "<leader>vws", function() vim.lsp.buf.workspace_symbol() end, vim.tbl_deep_extend("force", opts, { desc = "LSP 워크스페이스 심볼" }))
      vim.keymap.set("n", "<leader>vd", function() vim.diagnostic.setloclist() end, vim.tbl_deep_extend("force", opts, { desc = "LSP 오류 표시" }))
      vim.keymap.set("n", "[d", function() vim.diagnostic.goto_next() end, vim.tbl_deep_extend("force", opts, { desc = "다음 오류" }))
      vim.keymap.set("n", "]d", function() vim.diagnostic.goto_prev() end, vim.tbl_deep_extend("force", opts, { desc = "이전 오류" }))
      vim.keymap.set("n", "<leader>vca", function() vim.lsp.buf.code_action() end, vim.tbl_deep_extend("force", opts, { desc = "LSP 코드 수정" }))
      vim.keymap.set("n", "<leader>vrr", function() vim.lsp.buf.references() end, vim.tbl_deep_extend("force", opts, { desc = "LSP 참조" }))
      vim.keymap.set("n", "<leader>vrn", function() vim.lsp.buf.rename() end, vim.tbl_deep_extend("force", opts, { desc = "LSP 이름 변경" }))
      vim.keymap.set("i", "<C-h>", function() vim.lsp.buf.signature_help() end, vim.tbl_deep_extend("force", opts, { desc = "LSP 서명 도움말" }))
    end)

    require("mason").setup({})
    require("mason-lspconfig").setup({
      ensure_installed = {
        "gopls",
      },
      handlers = {
        lsp.default_setup,
        lua_ls = function()
          local lua_opts = lsp.nvim_lua_ls()
          require("lspconfig").lua_ls.setup(lua_opts)
        end,
      },
    })
  end,
}

Treesitter 문법 추가

문법 표시를 활성화하고 다른 플러그인에서 사용하는 트리시터 구문 트리를 지원하기 위해 go에 대한 문법을 설치하세요.

{
    "nvim-treesitter/nvim-treesitter",
    build = ":TSUpdate",
    config = function()
      local configs = require("nvim-treesitter.configs")

      configs.setup({
        ensure_installed = {
          "go",
        },
      })
    end,
}

Conform.nvim으로 서식 추가하기

이전에는 null-ls를 사용했지만 해당 플러그인이 보관되었고, 이제는 conform.nvim을 사용하여 서식을 설정하는 것을 선호합니다.

제가 개인적으로 서식을 트리거할 때 키를 누를 때 좋아하는데요, 만약 자동 저장을 선호한다면 conform.setup 내에 이 부분을 추가하세요.

format_on_save = {
  - 이 옵션들은 conform.format()로 전달됩니다.
  timeout_ms = 500,
  lsp_format = "fallback",
},

이제 conform.nvim을 설치하고 구성하고 Go 파일에 "gofmt"를 설정하는 플러그인 블록을 제공해 드렸습니다.

{
  "stevearc/conform.nvim",
  event = { "BufReadPre", "BufNewFile" },
  config = function()
    local conform = require("conform")

    conform.setup({
      formatters_by_ft = {
        go = { { "gofmt" } },
      },
    })

    vim.keymap.set({ "n", "v" }, "<leader>l", function()
      conform.format({
        lsp_fallback = true,
        async = false,
        timeout_ms = 1000,
      })
    end, { desc = "파일 또는 범위(시각적 모드에서)를 형식화합니다." })
  end,
}

이제 Neovim을 다시 시작하고 Go 파일을 열어서 자동 완성, 서식 지정 및 구문 강조가 예상대로 작동하는 것을 확인할 수 있어야 합니다.

디버거 설치 및 구성하기

이제 DAP(Debug Adapter Protocol)를 기반으로 하는 필수 디버거를 설정해보겠습니다.

Delve 및 nvim-dap-go 설치하기

저는 Mason을 사용하여 delve를 설치했습니다. 아직 많이 사용해보지는 않았지만 좋은 평가를 들었습니다. 더 나은 디버거가 있다고 판단할 경우 이 글을 업데이트하겠습니다.

다른 디버거를 사용하고 Delve보다 선호하는 경우 댓글로 알려주세요.

다시 mason.nvim을 사용하여 delve를 설치하세요.

{
    "williamboman/mason.nvim",
    dependencies = {
      "WhoIsSethDaniel/mason-tool-installer.nvim",
    },
    config = function()
      local mason = require("mason")
      local mason_tool_installer = require("mason-tool-installer")

      mason_tool_installer.setup({
        ensure_installed = {
          "gopls",
          "delve",
        },
      })
    end,
  }

이제 nvim-dap을 설치하고 구성하여 특정 언어에 맞는 dap을 연결하세요. 저는 nvim-dap-go를 사용합니다.

{
    "mfussenegger/nvim-dap",
    dependencies = {
      {
        "rcarriga/nvim-dap-ui",
        "nvim-neotest/nvim-nio",
        config = function(_, opts)
          local dap = require("dap")
          local dapui = require("dapui")
          dapui.setup(opts)
          dap.listeners.after.event_initialized["dapui_config"] = function()
            dapui.open({})
          end
          dap.listeners.before.event_terminated["dapui_config"] = function()
            dapui.close({})
          end
          dap.listeners.before.event_exited["dapui_config"] = function()
            dapui.close({})
          end
        end,
      },
      {
        "leoluz/nvim-dap-go",
        config = function()
          require("dap-go").setup()
        end,
      },
      {
        "jay-babu/mason-nvim-dap.nvim",
        dependencies = "mason.nvim",
        cmd = { "DapInstall", "DapUninstall" },
        opts = {
          automatic_installation = true,
          handlers = {},
          ensure_installed = {
            "delve"
          },
        },
      },
    },
  }

이제 Go 파일로 들어가서 중단점을 설정하고 디버그하실 수 있어요!

:lua require('dap-go').debug_test()

대안들

만약 여러 조각이 아닌 하나의 구성을 선호한다면 vim-go를 확인해보세요.

개인적으로 저는 각 부분을 구성하는 것만큼 확장 가능하지 않다고 생각하지만, 어떤 사람들은 이 방법을 선호합니다.

기타 자료

만약 비디오 형식의 안내를 선호한다면 아래 비디오를 확인해보세요.

위주제를 즐기시는 경우 저의 유튜브 채널도 마음에 드실 것 같아요. 좋은 하루 되세요!