Windows 운영체제는 시스템의 몇가지 중요한 정보를 변경을 수행하기 위해서 관리자 권한으로 실행되어져야 합니다. 예를 들어 COM 기반의 컴포넌트를 등록하기 위한 경우 관리자 권한이 아닌 경우 등록이 실패합니다. 아래의 코드는 C#으로 개발된 프로그램을 실행할 때 관리자 권한으로 프로그램이 실행될 수 있도록 하는데, Program.cs의 Main() 함수에 대한 전체 코드입니다.
[STAThread]
static void Main()
{
if (IsAdministrator() == false) // 관리자 권한으로 실행되지 않는 경우라면 ..
{
try
{
ProcessStartInfo procInfo = new ProcessStartInfo();
procInfo.UseShellExecute = true;
procInfo.FileName = Application.ExecutablePath;
procInfo.WorkingDirectory = Environment.CurrentDirectory;
procInfo.Verb = "runas";
Process.Start(procInfo);
}
catch (Exception ex)
{
// 사용자가 프로그램을 관리자 권한으로 실행하기를 원하지 않을 경우에 대한 처리
MessageBox.Show(ex.Message);
return;
}
} else { // 처음부터 프로그램은 관리자 권한으로 실행되고 있는 경우라면 ..
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new Form1());
}
}
위의 코드에서 IsAdministrator 라는 함수가 보이는데요. 이 함수는 아래와 같습니다.
public static bool IsAdministrator()
{
WindowsIdentity identity = WindowsIdentity.GetCurrent();
if(identity != null)
{
WindowsPrincipal principal = new WindowsPrincipal(identity);
return principal.IsInRole(WindowsBuiltInRole.Administrator);
}
return false;
}
위의 코드에서 참조하는 클래스를 인식하기 위해서는 다음을 import 문이 필요합니다.
using System.Security.Principal;
using System.Diagnostics;
제목이 참 마땅히 지을만한것도 없고.. 어여 이 곳에 정리해 내 머리속에서 지워버리고자 하는 마음에 지은 제목입니다.
상황은.. jetty를 사용해 지도 OpenAPI를 만들었고, 이 OpenAPI를 Spring 프레임워크를 기본으로 하는 웹 시스템에서 사용하는데, AJAX의 POST 방식으로 OpenAPI를 호출합니다. 이때 이 OpenAPI를 Spring 프레임워크가 아닌 환경에서는 Cross Domain 문제가 발생하지 않습니다. 물론 발생하지 않도록 OpenAPI 서버에 조치를 해두었습니다. 그런데.. Spring 프레임워크에서는 Cross Domain 에러가 발생합니다. 신기한건.. AJAX의 GET 방식은 Cross Domain 에러가 발생하지 않고 POST 만 발생합니다.
먼저 Cross Domain 문제는 아래의 코드를 OpenAPI 서버(jetty를 활용하는 서버)에 적용하여 해결할 수 있었습니다. 코드가 긴데.. 불필요한 코드가 있을거라 생각되지만 일단 모두 기재합니다.
FilterHolder holder = new FilterHolder(CrossOriginFilter.class);
holder.setInitParameter(CrossOriginFilter.ALLOWED_ORIGINS_PARAM, "*");
holder.setInitParameter(CrossOriginFilter.ACCESS_CONTROL_ALLOW_ORIGIN_HEADER, "*");
holder.setInitParameter(CrossOriginFilter.ALLOWED_METHODS_PARAM, "GET,POST,HEAD");
holder.setInitParameter(CrossOriginFilter.ALLOWED_HEADERS_PARAM, "X-Requested-With,Content-Type,Accept,Origin");
holder.setName("cross-origin");
FilterMapping fm = new FilterMapping();
fm.setFilterName("cross-origin");
fm.setPathSpec("*");
handler.addFilter(holder, fm);
위의 코드를 OpenAPI 서버에 적용하면 더 이상 Cross Domain 에러가 발생하지 않습니다만, 여전이 문제가 있습니다. POST 방식으로 데이터를 전달 하는데.. 이 데이터의 형식이 일반적으로 흔한 FORM 형태로 전달되어야 합니다. 그런데 Spring에서는 payload라는 형식으로 전달됩니다. 이에 대한 문제는 클라이언트 웹단에서 AJAX 호출시 headers 옵션값을 추가적으로 지정해 주면 해결됩니다. 즉, 아래처럼요.
솔찍히 Spring 프레임워크에서 발생하는 문제인지는 모르겠습니다. 단지 Spring 프레임워크가 아닌 환경에서는 문제가 발생하지 않았으므로, 아마도 Spring 프레임워크 환경에서 발생하는 문제라고 추측할 뿐이지만.. 여튼 이와 유사한 문제가 발생할 경우에 대한 해결책으로써 기록을 남깁니다.
JavaScript 언어를 이용해 SVG에 PieChart를 위한 도형을 생성하기 위한 코드를 함수로 정리해 둡니다. 최종 결과는 아래처럼 4개의 항목으로 구성된 가운데 구멍이 뚫린 모양입니다.
먼저 아래처럼 svg와 PieChart를 구성하는 4개의 항목을 path로 지정해 둡니다. (실제 활용시에는 이 부분을 모두 JavaScript 코드로 동적 생성하도록 해야 합니다)
위의 4개의 path에 대해 각각 파이차트의 구성 요소로 만들어 주는 함수는 아래와 같습니다.
function toPieChartItemPath(x, y, radiusIn, radiusOut, startAngle, endAngle) {
function _toXY(cX, cY, r, degrees) {
var rad = (degrees) * Math.PI / 180.0;
return {
x: cX + (r * Math.cos(rad)),
y: cY + (r * Math.sin(rad))
};
}
var startIn = _toXY(x, y, radiusIn, endAngle);
var endIn = _toXY(x, y, radiusIn, startAngle);
var startOut = _toXY(x, y, radiusOut, endAngle);
var endOut = _toXY(x, y, radiusOut, startAngle);
var arcSweep = (endAngle - startAngle) <= 180 ? "0" : "1";
var d = [
"M", startIn.x, startIn.y,
"L", startOut.x, startOut.y,
"A", radiusOut, radiusOut, 0, arcSweep, 0, endOut.x, endOut.y,
"L", endIn.x, endIn.y,
"A", radiusIn, radiusIn, 0, arcSweep, 1, startIn.x, startIn.y,
"z"
].join(" ");
return d;
}
이 함수의 인자를 설명하면, x와 y는 파이 차트의 중심점이고 radiusIn과 radiusOut은 파이차트의 내부 반지름과 외부 반지름 값입니다. 그리고 startAngle와 endAngle는 시작각도(3시 방향이 0도)와 종료각도(시계방향)입니다. 이 함수를 통해 앞서 구성한 svg와 path 요소를 파이차트로 구성하는 코드는 아래와 같습니다.
“소프트웨어 개발이 좋은 사람”이라는 블로그에서 d3를 이용해 아날로그 시계를 만든 글을 보고 코드를 살펴 보았습니다. d3는 개인적으로도 관심이 많고, 상당이 깊이 있게 공부를 했던(하지만 활용력은 아직까지도 얇은..) 자바스크립트 기반의 데이터 시각화 라이브러리입니다. 그 결과는 아래와 같습니다.
[xyz-ihs snippet=”D3-Clock”]
위의 시계에 대한 코드를 d3의 학습겸 해서 정리해 보고자 합니다. 먼저 시계에 대한 뷰는 아래처럼 오직 svg 태그 하나입니다.
시계의 크기에 해당하는 이 svg의 크기를 지정하기 위해 다음처럼 스타일을 지정합니다.
#clock {
width: 400px;
height: 400px;
}
시계의 구성요소는 시계의 형태인 큰 동그라미 부분, 12개의 숫자 부분, 시분초에 대한 바늘 부분으로 구성됩니다. 시분초에 대한 바늘은 Timer를 사용해 1초마다 갱신해 진짜 시계처럼 움직이도록 합니다.
스크립트 코드를 작성해 볼 것인데요. 가장 먼저 앞서 추가해둔 svg 요소를 d3의 선택자를 이용해 선택해 둡니다.
var clock = d3.select("#clock");
var center = parseInt(clock.style("height")) / 2;
var radius = center;
위의 코드는 추가적으로 svg의 높이값의 반값으로 중심점을 위한 center 변수와 시계의 반지름 크기값을 위한 radius 변수도 정의되어 있습니다.
앞서 시계는 3개의 부분으로 구성된다고 하였는데요. 다시 언급하면 시계의 형태인 큰 동그라미 부분, 12개의 숫자 부분, 시분초에 대한 바늘 부분입니다. 먼저 시계의 형태인 큰 동그라미 부분을 그리는 함수 drawFace를 반지름값(radius 변수)을 인자로 해 호출합니다.
drawFace(radius);
function drawFace(radius) {
clock.append("circle")
.attr("cx", center)
.attr("cy", center)
.attr("r", radius)
.attr("class", "face");
clock.append("circle")
.attr({
"cx": center,
"cy": center,
"r": radius * 0.1,
"fill": "#000"
});
}
위의 코드에서 face라는 class를 통해 스타일을 지정하고 있는데요. 이 .face에 대한 스타일 정의는 아래와 같습니다.
.face {
fill : #FFF;
stroke-width: 2px;
stroke: #000;
}
또 아래는 12개의 숫자 부분을 구성하는 코드입니다.
drawNumbers(radius);
function drawNumbers(radius) {
var pos = radius * 0.85;
for (var num = 1; num < 13; num++) {
var deg = 30 * num;
var x = pos * Math.cos(Math.PI * (deg - 90) / 180);
var y = pos * Math.sin(Math.PI * (deg - 90) / 180);
var cx = x + center;
var cy = y + center;
var text = clock.append("text")
.attr({ "x": cx, "y": cy, "class": "number" })
.text(num)
.style("font-size", radius * 0.15 + "px")
.attr("transform", "rotate(" + deg + ", " + cx + ", " + cy + ")");
}
}
위의 코드에서 number라는 class를 통해 숫자의 문자 스타일을 지정하고 있는데요. 이 .number에 대한 스타일 정의는 아래와 같습니다.
또 아래는 시, 분, 초에 대한 바늘을 구성하는 코드입니다. 시침, 분침, 초침을 각각 hourHand, minuteHand, secondHand 변수에 저장하고 있는데요. 이 변수를 통해 1초마다 각 침의 형태를 변경해 주게 됩니다.
var hourHand = drawHand(0, 0, radius*0.07);
var minuteHand = drawHand(0, 0, radius*0.05);
var secondHand = drawHand(0, 0, radius*0.01);
function drawHand(x, y, width) {
var hand = clock.append("line")
.attr({
"x1": center,
"y1": center,
"x2": x + center,
"y2": y + center,
"class": "hands" })
.style("stroke-width", width);
return hand;
}
위의 코드에서 hand라는 class를 통해 바늘의 스타일을 정의하고 있습니다. 이 .hand에 대한 스타일 정의는 아래와 같습니다.
.hands {
stroke: #000;
stroke-linecap: round;
}
이제 1초마다 제 시간에 맞게 바늘을 움직도록 하는 코드는 아래와 같습니다.
drawClock();
function drawClock() {
drawTime(radius);
setTimeout(drawClock, 1000);
}
위의 코드에서 1초마다 실행되는 drawTime의 함수가 보이는데요. 바로 이 drawTime이 3개의 바늘을 현재 시간에 맞게 그 형상을 변경해 주는 함수이며 아래와 같습니다.
function drawTime(radius){
var now = new Date();
var hour = now.getHours();
var minute = now.getMinutes();
var second = now.getSeconds();
//hour
var pos = radius * 0.5;
x = pos * Math.cos(Math.PI*((hour*30)-90+30/60*minute+30/60/60*second)/180);
y = pos * Math.sin(Math.PI*((hour*30)-90+30/60*minute+30/60/60*second)/180);
hourHand.attr({"x1": center, "y1": center, "x2": x+center, "y2": y+center});
//minute
pos = radius*0.65;
x = pos * Math.cos(Math.PI*((minute*6)-90+6/60*second)/180);
y = pos * Math.sin(Math.PI*((minute*6)-90+6/60*second)/180);
minuteHand.attr({"x1": center, "y1": center, "x2": x+center, "y2": y+center});
// second
pos = radius*0.9;
x = pos * Math.cos(Math.PI* ((second*6)-90)/180);
y = pos * Math.sin(Math.PI* ((second*6)-90)/180);
secondHand.attr({"x1": center, "y1": center, "x2": x+center, "y2": y+center});
}