'Functional World' 카테고리의 글 목록 (4 Page) :: iopeni - Think of C#

나는 어쩔 수 없는 초보 인가 보다..


아니면 너무 C++ 포인터에 젖어 있어 그런가.. 당췌 알 수 없지만...


오늘 하나를 또 배웠다...


Int32 v = 5;

Object o = v;

v = 123;


이렇게 코드를 작성 했을 때 단순하게 박싱을 업캐스팅을 이용한 포인터 라고만 이해 한다면 이건 정말 생각의 오류 였던 것이다.


o의 값은 여전히 5 이다.


박싱된 o와 v는 별개 인 것이다.


이렇게 생각 해 보면 몇가지 문제가 있다.


o는 레퍼런스 타입이다. 레퍼런스 타입이라는 것은 언제든 가베지 컬렉터가 수집하여 소거 하여야 하는데, Value 타입으로 부터 파생되었으므로 명시적으로 소거를 할 수 없다. 


더불어 박싱이 일어 나게 된다면 일어나는 만큼 값 복사가 될 것이다.


이는 퍼포먼스 측면에서도 메모리 사용 측면에서도 상당한 스트레스로 작용하게 될 것이다.


예전 무식하게 프로그램을 작성하던... 명시적인 타입의 전달이 역시나, 진리로 느껴지는 순간...


ValueType을 사용하기 싫어진다... 젠장.....

'Functional World > C#' 카테고리의 다른 글

Form.ActiveForm  (0) 2013.11.07
바이트 배열 취득  (0) 2013.10.01
IEnumerator 사용하기 - 기초  (0) 2013.08.11
Generic Collections  (0) 2013.07.15
C# 5.0 async, await  (0) 2013.07.05
Posted by 프로그래머란 카페인을 코드로 변환하는 기계다
,

세상 사람들이 Big Data를 외치고

많은 프로젝트들이 Big Data로 만들어지고


남들은 이미 다 하고 있는 Big Data를 뒤늦게 관심 가지게 되었습니다.


예전 어떤 친구에서 빅데이타는 아무것도 모르는 초보 프로그래머가 가장 접근 하기 쉬운 DB는 MongoDB라는 이야기를 들었던 기억이 있어서 MongoDB로 학습하기로 결정 하였습니다. 


일단 뭐 학습을 하려면 프로그램부터 다운 받아야 하니 구글에서 몽고디비 검색... 쉽게 찾을 수 있었습니다.


http://www.mongodb.org 


32bit - http://downloads.mongodb.org/win32/mongodb-win32-i386-2.4.6.zip

64bit - http://downloads.mongodb.org/win32/mongodb-win32-x86_64-2.4.6.zip


윈도우 프로그래머인 저는 윈도우 버젼으로... ^^ 다운 받았습니다.

이제 비쥬얼스튜디오에서... 몽고디비에 접속하여야 하므로 C#용 드라이버를 다운 로드 받아야 겠죠?


https://github.com/mongodb/mongo-csharp-driver/releases


일단 Visual Studio로 몽고디비를 사용해 보는 것은 뒤로 미루고 이 녀석이 어떻게 실행 되며 동작 되는지 부터 해봐야 겠기에...


압축 풀고... 

C:\mongoDB 생성 후 복사 

C:\mongoDB\data\db 폴더 생성


C:\mongoDB\bin\mongod --dbpath C:\mongodb\data\db


실행하면.. 디비 실행 완료




이제 시작 인가 봅니다.. 나의 몽고디비 학습기.... 빅데이타 학습기....


윈도우 서비스로 MongoDB 등록 하기.


mongod --install  --dbpath c:\mongodb\data\db --logpath c:\mongodb\log\mongo.log --logappend

http://docs.mongodb.org/manual/tutorial/install-mongodb-on-windows/


C:\mongoDB\bin\mongo.exe


>db.user.insert({name:"길동이", age:6})

>db.user.find({name:"길동이"})


메뉴얼 참고 하여 첫 인서트와 셀렉트를 날려 보니.... 오옷 이거 컬렉션 입니다. 

그것도 제너릭 컬렉션이죠 자료 타입의 인자가 몇개가 들어가던 프로퍼티를 자동으로 생성하는 컬렉션 입니다.


정말 재미 있을 듯 합니다. 열심히 공부 해봐야 겠죠?



Posted by 프로그래머란 카페인을 코드로 변환하는 기계다
,

예전 Chart를 그리고 싶다면 누가 뭐래도 ChartFX 가 최고 였던것 같습니다.

Microsoft Chart Control은 어딘지 모를 어색함에 이쁜 차트를 그리기는 쉽지 않았던것 같습니다.

아마도 GUI를 지원 하는 개발툴들 중 가장 허접한 디자인의 차트 였던것 같습니다.


.Net에서는 이런 예전의 차트 컨트롤을 버리고 아주 획기적으로 이쁜 차트로 탈바꿈 되었습니다.

Microsoft가 제공하는 Sample로 차트가 얼마나 이뻐 졌는지 확인해 보도록 하죠.


http://archive.msdn.microsoft.com/mschart


위 사이트로 가시면 차트 컨트롤 샘플을 받으실 수 있습니다.


위 샘플은 .Net Framework 4 기준의 샘플 입니다. 아래 링크를 보시면 3.5버젼의 샘플도 구하실수 있습니다.


일단.. 차트 컨트롤의 설명을 보면...


  • All supported chart types.
  • Data series, chart areas, axes, legends, labels, titles, and more.
  • Data Binding
  • Data manipulation, such as copying, splitting, merging, alignment, grouping, sorting, searching, filtering, and more.
  • Statistical formulas and financial formulas.
  • Advanced chart appearance, such as 3D, anti-aliasing, lighting, perspective, and more.
  • Chart rendering.
  • Events and Customizations.

이렇게 설명 하고 있습니다.

단지 이렇게만 보면 이 샘플의 위용을 느끼 실수 없을 텐데.

일단 다운 로드 받아서 설치 해 보도록 하죠.




다운로드 탭을 클릭 후 윈폼 버젼의 샘플을 다운 받습니다.




압축을 풀면 이런 파일들이 있습니다. 제일 하단 WinFormsChartSamples를 클릭하여 솔루션을 엽니다.


그리고 F5번 키를 눌러 실행을 해 보면... 아래 에러가..

이건 단순 코드 문제로.. 아무래도 한국 코드 페이지와 틀려서 생긴 문제로 별 문제는 아닙니다. 


코드를 확인해 보면...



위처럼 쟡 .... ?; 처럼 코드가 깨져 보입니다. 대충 살펴 보기에 쟡은 Demo 처럼 ?; 은 ."; 으로 바꿔 주면 동작 할 듯 합니다. 뭐 당연하죠?


이런 방법으로 모든 에러를 수정 하시면 됩니다. 그리고 실행을 해 보시면 다음과 같은 화면이 나옵니다.




아래 그림 처럼 몇개 선택을 해 보시면 챠트가 바뀌는 것을 보실 수 있습니다.




이 화면 상단에 C# Source 와 Visual Basic Source가 보이시나요? 저 탭을 클릭해 보시면 


저 챠트를 그리기 위해 필요한 샘플 코드가 보입니다.


아래 처럼 말이죠...


C# Code

using System.Windows.Forms.DataVisualization.Charting;
...

// Set series chart type
chart1.Series["Price"].ChartType = SeriesChartType.Stock;

// Set the style of the open-close marks
chart1.Series["Price"]["OpenCloseStyle"] = "Triangle";

// Show both open and close marks
chart1.Series["Price"]["ShowOpenClose"] = "Both";
        
// Set point width
chart1.Series["Price"]["PointWidth"] = "1.0";

// Set a dividend marker
chart1.Series["Price"].Points[5].MarkerImage = "DividentMarker.bmp";
chart1.Series["Price"].Points[5].MarkerImageTransparentColor = Color.White;
chart1.Series["Price"].Points[pointIndex].ToolTip = "#VALX{D}\n0.15 - dividend paid per share";
... 

Visual Basic Code

Imports System.Windows.Forms.DataVisualization.Charting
...

' Set series chart type
Chart1.Series("Price").ChartType = SeriesChartType.Stock

' Set the style of the open-close marks
Chart1.Series("Price")("OpenCloseStyle") = "Triangle"

' Show both open and close marks
Chart1.Series("Price")("ShowOpenClose") = "Both"

' Set point width
Chart1.Series("Price")("PointWidth") = "1.0"

' Set a dividend marker
Chart1.Series("Price").Points(5).MarkerImage = "DividentMarker.bmp"
Chart1.Series("Price").Points(5).MarkerImageTransparentColor = Color.White
Chart1.Series("Price").Points(pointIndex).ToolTip = "#VALX{D}\n0.15 - dividend paid per share"

이렇게 모든 타입의 챠트를 전부 어떤 코드로 생성 할 수 있는지 샘플로 만들어져 있습니다.


어떠신가요? 이 샘플 참고로 볼만하죠?


'Functional World > .Net Framework' 카테고리의 다른 글

호기심 해결  (0) 2017.01.13
Microsoft .Net Framework 4.5? 에 추가된 것들...  (0) 2013.11.01
Posted by 프로그래머란 카페인을 코드로 변환하는 기계다
,


Head First - Design Pattern 에 정의 하길....

Strategy Pattern에서는 알고리즘군을 정의하고 각각을 캡슐화하여 교환해서 사용할 수 있도록 만든다. 스트레티지를 활용하면 알고리즘을 사용하는 클라이언트와는 독립적으로 알고리즘을 변경 할 수 있다.


위 정의만을 놓고 보자면 도대체 이게 무슨 소리 인지 이해하기가 쉽지 않네요.


일단 스트레티지 패턴을 이해 하기 위하여, 몇 가지 사전 지식이 필요합니다.


1. 추상클래스

2. 인터페이스

3. 상속

4. 업.다운캐스팅


수 많은 프로그래밍 구루들이 이야기 하길 사실 프로그래밍을 하기 위하여 꼭 알아야 하는 것으로 상속과 추상클래스, 인터페이스 보다 중요한 것은 없다고 이야기들을 합니다.


스트래티지 패턴은 위에서 언급한 4가지 개념으로 만들어 집니다. 만일 위 4가지 개념중 한가지라도 좀 미흡하다 싶은 분께서는 먼저 저 4가지 개념 부터 다시 학습 하신다음에 스트레티지 패턴을 보시면 이해 하기가 훨씬 용이할 겁니다.


이제 스트레티지 패턴을 풀어 보도록 하죠


한국 사람이라면 누구나 수저를 이용하여 밥을 먹을 껍니다. 그런데 사실 우리 와이프는 저보다 젓가락질을 잘 못합니다.


이게 바로 스트레티지 패턴의 전형적인 예 입니다.


어떤 사람은 밥을 먹을때 포크와 숟가락 으로만 먹을 것이며, 어떤 사람은 숟가락으로만 ... 어떤 사람은 젓가락으로만... 뭐 이렇게  밥을 먹을때도 각각의 사람들은 각각의 자기만의 밥 먹는 방법이 있을 겁니다. 그냥 이렇게만 한정 지어 버린다면 이 문제를 상속과 추상클래스 만으로 풀어 내려고 해도 됩니다. 아주 훌륭한 방법이 되겠죠.


그런데 어느날 갑자기 우리 와이프가 젓가락질을 겁내 열심히 연습해서 저보다 젓가락질을 잘 하게 될 수도 있을 겁니다. 그럼 행동 패턴이 바뀌게 됩니다. 지금은 젓가락질을 잘 못하기 때문에 숟가락으로만 밥을 먹다가... 어느날 갑자기 수저로 밥을 먹게 되어 버린거죠. 행위의 속성이 한가지 추가 되어 버린겁니다. 


이렇게 되어 버리면 상속이나 추상으로만 놓고 보면 추 후 유지보수측면에서만 바라 보더라도 좀 곤란한 상황이 발생 하게 될 수도 있습니다. 


좀 더 확장해서 생각해 보면 상속이나 추상으로만 해결 하였을 경우 우리 와이프가 젓가락질을 잘 못한다고 우리 와이프의 뱃속에서 태어나 자란 아이가 젓가락질을 잘 못할 수도 있겠지만... 저를 닮아서 잘 할 수도 있다는 점이죠... 


결국 행위에 대한 정의는 쉽게 변경 될 수 있으나 상속을 통해서 행위에 대한 정의를 변형 하려고 하면 부모로 부터 상속 받은 문제등으로 인하여 곤란한 상황이 될 수 있을 거라는 뜻 입니다. 


이런 문제를 해결 하기 위한 것이 인터페이스이며, 이 인터페이스를 활용하여 우리의 프로그래밍 구루 몇분께서 스트레티지 패턴을 만들어 놓으셨습니다.


스트레티지 패턴을 구현하기에 앞서 우리가 먼저 외워야 할 것들이 있습니다.


1. Application에서 달라지는 부분(행위)과 달라지지 않는 부분(몸체)을 구분하여 분리 하여야 한다.

2. 상위 형식에 맞추어 프로그래밍 하라.(업캐스팅) - A=B 이다가 아니라. A에는 B가 있다는 것이 훨씬 좋을 수 있으므로 인터페이스를 선언하고 상위 형식으로 인터페이스를 이용하는 방법 이 좋을 수 있다는 뜻 입니다.



using System;

namespace Strategy
{
    public class MiniDuckSimulator
    {
        static void Main(string[] args)
        {
            Duck mallard = new MallardDuck();
            mallard.Display();
            mallard.PerformQuack();
            mallard.PerformFly();

            Console.WriteLine("");

            Duck model = new ModelDuck();
            model.Display();
            model.PerformFly();

            //런타임에 날 수 없는 장난감 오리를 겁내 빨리 날도록 바꿨어요.
            model.FlyBehavior = new FlyRocketPowered();
            model.PerformFly();

            Console.ReadKey();
        }
    }

    #region Duck

    public abstract class Duck
    {
        public IFlyBehavior FlyBehavior { get; set; }
        public IQuackBehavior QuackBehavior { get; set; }

        public abstract void Display();

        public void PerformFly()
        {
            // A에는 B가 있다.
            FlyBehavior.Fly();
        }

        public void PerformQuack()
        {
            QuackBehavior.Quack();
        }

        public void Swim()
        {
            Console.WriteLine("모든 오리는 물위에 뜰 수 있어요.");
        }

    }

    public class MallardDuck : Duck
    {
        public MallardDuck()
        {
            //인터페이스 업캐스팅
            QuackBehavior = new LoudQuack();
            FlyBehavior = new FlyWithWings();
        }

        override public void Display()
        {
            Console.WriteLine("나는 청둥오리예요");
        }
    }

    public class ModelDuck : Duck
    {
        public ModelDuck()
        {
            // 인터페이스 업캐스팅
            QuackBehavior = new LoudQuack();
            FlyBehavior = new FlyNoWay();
        }

        override public void Display()
        {
            Console.WriteLine("나는 실제 오리가 아니라 장난감 오리예요.");
        }
    }

    #endregion

    #region FlyBehavior

    public interface IFlyBehavior
    {
        void Fly();
    }

    public class FlyWithWings : IFlyBehavior
    {
        public void Fly()
        {
            Console.WriteLine("나는 날아 다녀요.");
        }
    }
    public class FlyNoWay : IFlyBehavior
    {
        public void Fly()
        {
            Console.WriteLine("나는 날지 못해요!");
        }
    }
    public class FlyRocketPowered : IFlyBehavior
    {
        public void Fly()
        {
            Console.WriteLine("나는 겁내 빨리 날아다녀요!");
        }
    }
    #endregion

    #region QuackBehavior

    public interface IQuackBehavior
    {
        void Quack();
    }

    public class LoudQuack : IQuackBehavior
    {
        public void Quack()
        {
            Console.WriteLine("크게 소리내요");
        }
    }

    public class MuteQuack : IQuackBehavior
    {
        public void Quack()
        {
            Console.WriteLine("나는 조용한 오리라구요.");
        }
    }

    public class Squeak : IQuackBehavior
    {
        public void Quack()
        {
            Console.WriteLine("작게 소리내요.");
        }
    }
    #endregion
}



Posted by 프로그래머란 카페인을 코드로 변환하는 기계다
,

열거자를 사용하기 위한 방법이다.


1. IEnumerator를 상속 받는 방법..

using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace IEnumeratorTest
{
    class myEnumerator : IEnumerator    
    {
        string[] whoareyou;
        int position = -1;

        public object Current
        {
            get { return whoareyou[position]; }
        }

        public bool MoveNext()
        {
            if (position < whoareyou.Length - 1)
            {
                position++;
                return true;
            }
            else return false;
        }

        public void Reset()
        {
            position = -1;
        }

        public myEnumerator(string[] inNames)
        {
            whoareyou = new string[inNames.Length];
            for (int i = 0; i < inNames.Length; i++)
                whoareyou[i] = inNames[i];
        }

    }
}


 종류

 유형 

 기능

 Current

 속성 

 읽기 전용 속성으로 현재 위치의 아이템을 object로 반환 

 MoveNext 

 메서드

 다음 위치로 이동, 다음 아이템 존재 true, 아니면 false 

 Reset

 메서드

 초기 상태로 위치를 설정


2. IEnumerator<T>를 상속 받는 방법.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace IEnumeratorTest
{
    class genericEnumerator : IEnumerator<string>
    {
        string[] strPlayer;
        int position = -1;

        public string Current
        {
            get { return strPlayer[position]; }
        }

        public void Dispose() { }

        object System.Collections.IEnumerator.Current
        {
            get { return strPlayer[position]; }
        }

        public bool MoveNext()
        {
            if (position < strPlayer.Length - 1)
            {
                position++;
                return true;
            }
            else return false;
        }

        public void Reset()
        {
            position = -1;
        }

        public genericEnumerator(string[] inNames)
        {
            strPlayer = new string[inNames.Length];

            for (int i = 0; i < inNames.Length; i++)
            {
                strPlayer[i] = inNames[i];
            }
        }
    }
}



 종류

 유형

 기능

 Current

 속성

 IEnumerator로 부터 상속된 속성 현재 위치의 아이템을 Object로 반환

 Current

 속성

 T Current {get;}의 형태로 T로 반환

 Dispose 메서드 파일 스트림과 같은 관리되지 않는 자원을 닫거나 해제할 때 사용

 MoveNext

 메서드

 다음 위치로 이동, 다음 아이템이 있으면 true 없으면 false

 Reset

 메서드

 초기 위치로 설정


Main

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace IEnumeratorTest
{
    class Program
    {
        static void Main(string[] args)
        {
            myEnumerator my = 
                new myEnumerator(new string[] { "전우치", "홍길동", "머털도사" });
            while (my.MoveNext())
            {
                string strName = (string)my.Current;
                Console.WriteLine("{0}", strName);
            }


            genericEnumerator str = 
                new genericEnumerator(new string[] { "베트맨", "슈퍼맨", "아이언맨" });
            while (str.MoveNext())
            {
                string strName = (string)str.Current;
                Console.WriteLine("{0}", strName);
            }

            Console.ReadLine();
        }
    }
}



'Functional World > C#' 카테고리의 다른 글

바이트 배열 취득  (0) 2013.10.01
boxing 에 대한 생각의 오류....  (0) 2013.09.30
Generic Collections  (0) 2013.07.15
C# 5.0 async, await  (0) 2013.07.05
Runtime에 클래스 맴버를 취득 하고자 할 경우….  (0) 2013.07.04
Posted by 프로그래머란 카페인을 코드로 변환하는 기계다
,

제너릭 컬렉션 이름

기능

List<T>

Array List와 유사한 기능을 수행하는 동적 배열

LinkedList<T>

이중 연결 리스트 구현에 사용

SortedList<TKey, TValue>

키와 값의 쌍으로 구성된 정렬된 리스트

Stack<T>

Last in First out

Queue<T>

First in First out

HashSet<T>

Hash Table을 이용하여, 구별되는 값들의 집합의 구현

SortedSet<T>

정렬된 순서대로 관리되는 집합

Dictionary<TKey, TValue>

HashTable 클래스와 유사하게 키, 값의 쌍으로 저장

SortedDictionary<TKey, TValue>

정렬된 키, 값의 쌍으로 저장한 리스트

 

Sample :

String[] player = { "전우치" , "홍길동", "영구" };

List<string> koreaplayer = new List<string>(player);

foreach (string tt in koreaplayer) Console.WriteLine(tt);

 

 

Posted by 프로그래머란 카페인을 코드로 변환하는 기계다
,
public class Form1 : Form
{
    public Form1()
    {
        InitializeComponent();
    }
    public async Task SaveFile()
    {
        await Task.Run(()=>
            using(FileStream fs = new FileStream("trashfile.bin",
                                                 FileMode.CreateNew))
            {
                BinaryWriter bw = new BinaryWriter(fs);
                for(int i = 0; i < 100000000; i++)
                    bw.Write(i);
            }
        });
    }
    public async void btnOK_Click(object sender, EventArgs e)
    {
        await SaveFile();
        MessageBox.Show("Complete.");
    }
}

비동기 쓰레드를 너무 쉽게 구현 할 수 있어요...

Posted by 프로그래머란 카페인을 코드로 변환하는 기계다
,

갑작스레 궁금증이 생겼다…

 

비졀 스튜디오에 객체 이름을 찍으면 객체의 멤버를 표시 하는 것 처럼..

런타임중 객체의 멤버를 취득 할 수 있는 방법은 없을까????? 라는 호기심…

 

아래와 같이 하면 된다… ㅋㅋ

using System.Reflection;

//취득하고자 하는 메소드 안에서 다음과 같이….
MemberInfo[] myMemberInfo;
Type myType = mycls.GetType();

//동적 형성된 멤버를 취득 한다.
myMemberInfo = myType.GetMembers(BindingFlags.Public |
                                 BindingFlags.Instance |
                                 BindingFlags.NonPublic);
//화면에 리스트 박스를 하나 추가 해둠..
this.listBox1.DataSource = myMemberInfo;


간단 Sample ㅋㅋ

'Functional World > C#' 카테고리의 다른 글

boxing 에 대한 생각의 오류....  (0) 2013.09.30
IEnumerator 사용하기 - 기초  (0) 2013.08.11
Generic Collections  (0) 2013.07.15
C# 5.0 async, await  (0) 2013.07.05
Func<TResult>() 대리자  (0) 2013.07.04
Posted by 프로그래머란 카페인을 코드로 변환하는 기계다
,

 

입력 파라미터가 없는 Func대리자의 사용법이다..

Func<string> myDel = Hello;

if (myDel != null) Console.WriteLine(myDel());

static string Hello()

{

string strHello = "안녕하세요?";

return strHello;

}

 

만약 입력 파라미터가 있다면 다음과 같이 할 수 있습니다.

 

Func<T, TResult> 대리자

public delegate TRsult Func<in T1, .... , out TResult>(T1 arg1,....)

 

입니다.

 

Func<string, int, string> myDel = invitedMember;

if (myDel != null) myDel("동이", 3);

 

static string infitedMember(string name, int grade)

{

return name;

}

 

 

'Functional World > C#' 카테고리의 다른 글

boxing 에 대한 생각의 오류....  (0) 2013.09.30
IEnumerator 사용하기 - 기초  (0) 2013.08.11
Generic Collections  (0) 2013.07.15
C# 5.0 async, await  (0) 2013.07.05
Runtime에 클래스 맴버를 취득 하고자 할 경우….  (0) 2013.07.04
Posted by 프로그래머란 카페인을 코드로 변환하는 기계다
,
작은 규모의 프로그램을 하나 만들고 있었다. CodeJock을 이용하여...


위의 내용 처럼 리본바 메뉴와 왼쪽에 Pane 하나를 붙이고 싶었는데... 이게 당췌.. 생각 처럼 되지 않았다.

몇일동안 오만가지 방법을 적용해보고.. 고쳐보고.. 하다 하다 안되서.. ^^ 
아는 동생에게 질문...

아주 멋진 답변을 듣고야 말았다...

동생 가라사대----
"이거 먼가 이상해요 콤퍼넌트 올리는 순서에 따라서도 영향을 받더라구요....."

그랬다.. 아무 생각 없이 CommandBar 를 먼저 올리고.. Docking 을 뒤에 올렸더니 위와 같은 화면이 되질 않았다... 

속으로 욕만 나오는... 뭐 이런게 다 있어??? 띠불.... 

그랬다...

먼저 Docking 컴포넌트를 올리고 그 다음에 CommandBar를 올려야 하는 것이었다....

이유가 멀까.. 댄장~~~~

'Functional World > Other' 카테고리의 다른 글

Intel Turobo Memory - ITMServ  (0) 2010.10.25
Posted by 프로그래머란 카페인을 코드로 변환하는 기계다
,