๋ฐ˜์‘ํ˜•

Redmine REST API

REST API๋ž€ HTTP ํ”„๋กœํ† ์ฝœ์„ ํ™œ์šฉํ•˜์—ฌ ๋ฐ์ดํ„ฐ๋ฅผ ์ฃผ๊ณ ๋ฐ›๋Š” ๊ฒƒ์„ ์˜๋ฏธํ•˜์ฃ . REST API์— ๋Œ€ํ•œ ์ข€ ๋” ์ž์„ธํ•œ ๋‚ด์šฉ์€ ์•„๋ž˜ ๋งํฌ๋ฅผ ์ฐธ๊ณ ํ•˜์‹œ๋ฉด ์ดํ•ดํ•˜๋Š”๋ฐ ๋„์›€์ด ๋ฉ๋‹ˆ๋‹ค.

[.Net] C#์—์„œ Open API ๊ณ ์†๋„๋กœ ๊ตํ†ต๋Ÿ‰ ์ •๋ณด ์–ป๊ธฐ (์˜ˆ์ œ ํฌํ•จ)

 

[.Net] C#์—์„œ Open API ๊ณ ์†๋„๋กœ ๊ตํ†ต๋Ÿ‰ ์ •๋ณด ์–ป๊ธฐ (์˜ˆ์ œ ํฌํ•จ)

์†Œ๊ฐœ ์ €๋Š” ๋จธ์‹  ๋น„์ „ ๋ถ„์•ผ์— ๊ทผ๋ฌดํ•˜๊ณ  ์žˆ๊ณ , ์ฃผ๋กœ ๋น„์ „ ๊ด€๋ จ ํ”„๋กœ๊ทธ๋žจ์„ ๊ฐœ๋ฐœํ•˜๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค. ์‚ฌ์‹ค ๋งค์ผ ํ•˜๋˜ ๊ฒƒ๋งŒ ๊ฐœ๋ฐœํ•˜๋‹ค ๋ณด๋‹ˆ ์ง€๋ฃจํ•˜๊ธฐ๋„ ํ•˜๊ณ , ๊ฐœ๋ฐœ์˜ ๋ฐฉํ–ฅ์„ฑ์ด ๋„ˆ๋ฌด ํญ์ด ์ข์€ ๋Š๋‚Œ์ด ๋“ค์—ˆ

luckygg.tistory.com

๊ทธ๋Ÿฐ๋ฐ ๋ ˆ๋“œ๋งˆ์ธ๋„ REST API๋ฅผ ํ†ตํ•ด ๋ฐ์ดํ„ฐ์— ์ ‘๊ทผํ•  ์ˆ˜ ์žˆ์œผ๋ฉฐ, API๋Š” XML ๋ฐ JSON ํ˜•์‹ ๋ชจ๋‘ ์ง€์›ํ•ฉ๋‹ˆ๋‹ค. ๊ทธ๋Ÿผ ์–ด๋–ค ๋ฐ์ดํ„ฐ์— ์ ‘๊ทผํ•  ์ˆ˜ ์žˆ๋Š”์ง€ ์•Œ์•„๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค.

Redmine REST API ๋ชฉ๋ก

REST API๋กœ ์ ‘๊ทผ ๊ฐ€๋Šฅํ•œ ํ•ญ๋ชฉ๊ณผ ํ˜„์žฌ ์ œ๊ณต๋˜๋Š” API์˜ ๊ฐœ๋ฐœ ์ƒํƒœ๋Š” ์•„๋ž˜ ํ‘œ์™€ ๊ฐ™์Šต๋‹ˆ๋‹ค.

ํ•ญ๋ชฉ ์ƒํƒœ ์„ค๋ช…
์ผ๊ฐ(Issues) Stable  
ํ”„๋กœ์ ํŠธ(Projects) Stable  
ํ”„๋กœ์ ํŠธ ๋ฉค๋ฒ„๊ด€๋ฆฌ(Project memberships) Alpha  
์‚ฌ์šฉ์ž(Users) Stable  
์‹œ๊ฐ„ ๋‚˜์—ด(Time entries) Stable ์‹œ๊ฐ„(๋˜๋Š” ๊ธฐ๊ฐ„)์œผ๋กœ ํ•„ํ„ฐ๋ง
๋‰ด์Šค(News) Prototype index ์ „์šฉ ํ”„๋กœํ† ํƒ€์ž… ๊ตฌํ˜„
์—ฐ๊ด€๋œ ์ผ๊ฐ(Issue relations) Alpha  
๋ฒ„์ „(Versions) Alpha Wiki ํŽ˜์ด์ง€์˜ ๋ฒ„์ „์„ ํ™•์ธ
์œ„ํ‚ค ํŽ˜์ด์ง€(Wiki pages) Alpha  
๊ฒ€์ƒ‰ ์–‘์‹(Queries) Alpha  
์ฒจ๋ถ€ ํŒŒ์ผ(Attachments) Beta  
์ผ๊ฐ ์ƒํƒœ(Issue statuses) Alpha  
์ถ”์ (Trackers) Alpha *์ผ๊ฐ ๊ด€๋žŒ์ž๋ฅผ ์˜๋ฏธํ•˜๋Š” ๊ฒƒ ๊ฐ™์Šต๋‹ˆ๋‹ค
์—ด๊ฑฐ(Enumerations) Alpha ์กฐ๊ฑด์— ๋”ฐ๋ฅธ ์ผ๊ฐ ๋ชฉ๋ก์„ ๋ฐ˜ํ™˜ํ•ฉ๋‹ˆ๋‹ค
์—ญํ• (Roles) Alpha  
๊ทธ๋ฃน(Groups) Alpha  
์‚ฌ์šฉ์ž ์ •์˜ ํ•ญ๋ชฉ(Custom fields) Alpha  
๊ฒ€์ƒ‰(Search) Alpha  
ํŒŒ์ผ(Files) Alpha  
๋‚ด ๊ณ„์ •(My account) Alpha  

๊ฐ ํ•ญ๋ชฉ ๋ณ„ ์ƒํƒœ์˜ ์˜๋ฏธ๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™์Šต๋‹ˆ๋‹ค. Stable์ด ๋‹จ์–ด ๊ทธ๋Œ€๋กœ ๊ฐ€์žฅ ์•ˆ์ •์ ์ด๋ผ๋Š” ์˜๋ฏธ์ด์ฃ .

์ƒํƒœ ์„ค๋ช…
Stable ๊ธฐ๋Šฅ ๊ตฌํ˜„์ด ์™„๋ฃŒ๋์œผ๋ฉฐ, ์•ž์œผ๋กœ ์ฃผ์š” ๋ณ€๊ฒฝ ๊ณ„ํš์ด ์—†๋Š” ์ƒํƒœ
Beta ์ผ๋ถ€ ๋ฒ„๊ทธ ๋˜๋Š” ๋ˆ„๋ฝ๋œ ์‚ฌ์†Œํ•œ ๊ธฐ๋Šฅ์ด ์žˆ๋Š” ์ƒํƒœ
Alpha ์ฃผ์š” ๊ธฐ๋Šฅ์ด ์žˆ์œผ๋ฉฐ ์‚ฌ์šฉ์ž์˜ ํ”ผ๋“œ๋ฐฑ์ด ํ•„์š”ํ•œ ์ƒํƒœ
Prototype ์•„์ฃผ ๊ฐ„๋‹จํžˆ ๊ตฌํ˜„๋์œผ๋ฉฐ, ์ฃผ์š” ๋ณ€๊ฒฝ ์‚ฌํ•ญ์ด ์žˆ์„ ์ˆ˜ ์žˆ๋Š” ์ƒํƒœ

REST API๋ฅผ ์‚ฌ์šฉํ•˜๊ธฐ ์œ„ํ•œ ์ค€๋น„ ๋‹จ๊ณ„

1. REST ์›น ์„œ๋น„์Šค ํ™œ์„ฑํ™”ํ•˜๊ธฐ

Redmine REST API๋ฅผ ์‚ฌ์šฉํ•˜๋ ค๋ฉด API ํ™œ์„ฑํ™” ์„ค์ •์ด ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค. ๊ด€๋ฆฌ์ž ๊ณ„์ •์œผ๋กœ ๋กœ๊ทธ์ธํ•œ ํ›„ ๊ด€๋ฆฌ์˜ ์„ค์ • ๋ฉ”๋‰ด๋ฅผ ๋“ค์–ด๊ฐ‘๋‹ˆ๋‹ค. ๊ทธ๋ฆฌ๊ณ  ์•„๋ž˜์˜ ํ™”๋ฉด์—์„œ REST ์›น ์„œ๋น„์Šค ํ™œ์„ฑํ™” ๋ฒ„ํŠผ์„ ์ฒดํฌํ•˜๊ณ  ์ €์žฅํ•ฉ๋‹ˆ๋‹ค.

REST ์›น ์„œ๋น„์Šค ํ™œ์„ฑํ™”ํ•˜๊ธฐ
REST ์›น ์„œ๋น„์Šค ํ™œ์„ฑํ™”ํ•˜๊ธฐ

2. API ์ ‘๊ทผํ‚ค ํ™•์ธํ•˜๊ธฐ

๋กœ๊ทธ์ธ ํ•œ ์ƒํƒœ์—์„œ ๋‚ด ๊ณ„์ •(My account) ๋ฉ”๋‰ด๋ฅผ ํด๋ฆญํ•ฉ๋‹ˆ๋‹ค. ๊ทธ๋ฆฌ๊ณ  ์ขŒ์ธก์˜ API ์ ‘๊ทผํ‚ค๋ฅผ ํ™•์ธํ•ฉ๋‹ˆ๋‹ค.

API ์ ‘๊ทผํ‚ค ํ™•์ธ
API ์ ‘๊ทผํ‚ค ํ™•์ธ

๋‚˜์ค‘์— ์ด ํ‚ค๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ๋ ˆ๋“œ๋งˆ์ธ API์— ์ ‘๊ทผํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

API ์ ‘๊ทผํ‚ค
API ์ ‘๊ทผํ‚ค

C#์—์„œ Redmine REST API ์ ‘๊ทผํ•˜๊ธฐ

ExamRedmineApi ์ด๋ฆ„์œผ๋กœ Windows Forms ์•ฑ ํ”„๋กœ์ ํŠธ๋ฅผ ์ƒ์„ฑํ•ฉ๋‹ˆ๋‹ค.

Windows Forms ์•ฑ ํ”„๋กœ์ ํŠธ ์ƒ์„ฑํ•˜๊ธฐ
Windows Forms ์•ฑ ํ”„๋กœ์ ํŠธ ์ƒ์„ฑํ•˜๊ธฐ

NuGet์—์„œ redmine ์„ ๊ฒ€์ƒ‰ํ•˜๊ณ  redmine-api๋ฅผ ์„ ํƒํ•˜์—ฌ ์„ค์น˜ํ•ฉ๋‹ˆ๋‹ค. ๋งŒ์•ฝ, NuGet์—์„œ ์ œ๊ณตํ•˜๋Š” redmine api๋ฅผ ์‚ฌ์šฉํ•˜๊ธธ ์›์น˜ ์•Š๋‹ค๋ฉด ์ง์ ‘ REST API๋ฅผ ์ œ์–ดํ•˜๋ฉด ๋ฉ๋‹ˆ๋‹ค. ๋ณธ๋ฌธ์—์„œ๋Š” ์‰ฝ๊ฒŒ ์‚ฌ์šฉํ•˜๊ธฐ ์œ„ํ•ด NuGet์„ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค.

NuGet redmine ํŒจํ‚ค์ง€ ์„ค์น˜
NuGet redmine ํŒจํ‚ค์ง€ ์„ค์น˜

Form UI๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™์ด ๋ฒ„ํŠผ๊ณผ ๋ฆฌ์ŠคํŠธ ๋ทฐ๋ฅผ ๋ฐฐ์น˜ํ–ˆ์Šต๋‹ˆ๋‹ค.

UI ๋ฐฐ์น˜
UI ๋ฐฐ์น˜

์ฝ”๋“œ๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™์ด ์ž‘์„ฑํ•ฉ๋‹ˆ๋‹ค.

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using Redmine.Net.Api;
using Redmine.Net.Api.Types;
using System.Collections.Specialized;

namespace ExamRedmineApi
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

        private void buttonRefresh_Click(object sender, EventArgs e)
        {
            try
            {
                //์กฐํšŒํ•  ๋ ˆ๋“œ๋งˆ์ธ ํ”„๋กœ์ ํŠธ์˜ ๋ฉ”์ธ ์ฃผ์†Œ
                string host = "";
                //API ํ‚ค
                string apiKey = "";
                var manager = new RedmineManager(host, apiKey);
                //์ผ๊ฐ ์ƒํƒœ๊ฐ€ ๋‹ซํ˜€์žˆ๋Š” ๊ฒƒ๋งŒ
                var parameters = new NameValueCollection { { RedmineKeys.STATUS_ID, "closed" } };
                //2022-01-1~2022-02-01 ์‚ฌ์ด ์กฐ๊ฑด
                parameters.Add(RedmineKeys.CREATED_ON, "><2022-01-01|2022-02-01");
                //์ผ๊ฐ ๋ชฉ๋ก ๋ฐ›๊ธฐ
                var issues = manager.GetObjects<Issue>(parameters);
                foreach(var issue in issues)
                {
                    ListViewItem item = new ListViewItem();
                    item.Text = issue.Id.ToString();
                    item.SubItems.Add(issue.Author.Name);
                    item.SubItems.Add(issue.Subject.ToString());

                    listViewIssues.Items.Add(item);
                }
            }
            catch(Exception exc)
            {
                MessageBox.Show(exc.Message);
            } 
        }
    }
}

์ฝ”๋“œ๊ฐ€ ์•„์ฃผ ๊ฐ„๋‹จํ•˜์ฃ ? ์‹ค์ œ ๋™์ž‘ ๊ฒฐ๊ณผ๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™์Šต๋‹ˆ๋‹ค.

๋™์ž‘ ํ™”๋ฉด
๋™์ž‘ ํ™”๋ฉด

parameters์— ์—ฌ๋Ÿฌ ์กฐ๊ฑด์„ ์ถ”๊ฐ€ํ•˜์—ฌ ํ•„ํ„ฐ๋ง๋œ ์ž๋ฃŒ๋ฅผ ์กฐํšŒํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. NuGet Redmine API์— ๊ด€ํ•œ ์‚ฌ์šฉ ๋ฐฉ๋ฒ•์€ ์•„๋ž˜ ๋งํฌ๋ฅผ ์ฐธ๊ณ ํ•˜์„ธ์š”.

https://github.com/zapadi/redmine-net-api/wiki

 

GitHub - zapadi/redmine-net-api: .Net API for Redmine bug/task management systems.

.Net API for Redmine bug/task management systems. Contribute to zapadi/redmine-net-api development by creating an account on GitHub.

github.com

์•„๋ž˜์˜ ์ƒ˜ํ”Œ ์ฝ”๋“œ๋„ ์ฐธ๊ณ ํ•˜์„ธ์š”.

ExamRedmineApi.zip
5.33MB

๋ฐ˜์‘ํ˜•