반응형

웹서버로 만든 웹페이지에서 JSON 데이터를 분석하고 테이블이나 리스트등 원하는 형식으로 보여 줄 수 있다.


2019/10/08 - [Linux] - Build your own web server with Fedora linux - 페도라 리눅스로 간단한 웹서버 만들기


웹페이지에 보여 줄 데이터를 준비한다.


데이터를 JSON 형식으로 변환 한다. 아래 링크에서 간단히 변환 할 수 있다.

https://shancarter.github.io/mr-data-converter/


변환된 데이터는 data.json 파일로 저장 한다.


html/data 디렉토리를 만들고 data.json 파일을 저장 한다.



아래 파일과 같은 내용으로 index.php를 만들고 html 디렉토리에 저장 한다.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
<?php
    $file_contents = file_get_contents('./data/data.json');
    $data = json_decode($file_contents, true);
?>
 
<!DOCTYPE html>
<html>
    <head>
        <meta charset="UTF-8">
        <title>PHP JSON parser sample</title>
    </head>
   
    <body>
    <table border="1" bordercolor="blue">
 
    <tr bgcolor="yellow">
    <th> 날짜 </th>
    <th> 아이템 </th>
    <th> 가격 </th>
    <th> 수량 </th>
    <th> 합계 </th>
    </tr>
 
    <?php foreach ($data as $row) { ?>
        <tr align="center">
 
        <td> <?php print $row['date']; ?> </td>
        
        <td> <?php print $row['item']; ?> </td>
        
        <td> <?php print $row['price']; ?> </td>
        
        <td> <?php print $row['quantity']; ?> </td>
        
        <td> <?php print $row['total']; ?> </td>
 
        </tr>
        
    <?php } ?>
 
    </table>
 
 
    <?php
    foreach ($data as $row) {
        print $row['date'];
        print ' , ';
        print $row['item'];
        print ' , ';
        print $row['price'];
        print ' , ';
        print $row['quantity'];
        print ' , ';
        print $row['total'];
        print '<br />';
    }
    ?>
 
 
    <?php
    foreach ($data as $row) {
        printf("<li>%s %s %s %s %s</li>\n", $row['date'], $row['item'], $row['price'], $row['quantity'], $row['total']);
    }
    ?>
    
    </body>
 
</html>



서버에 접속하면 data.json의 내용이 index.php에 설정한대로 표시된다.


반응형
Posted by J-sean
:
반응형

Raspbian을 설치하고 업데이트, 한글, 고정 IP, VNC, Samba 설정등을 진행 한다.

 

기본 사용자: pi

기본 암호: raspberry

 

sudo apt-get update

 

sudo apt-get upgrade

 

Preferences - Add/Remove Software 클릭

 

Nanum Korean fonts 설치

 

Nabi 설치

 

im-config 설치

 

 

 

Preferences - Input Method 클릭

 

OK 클릭

 

Yes 클릭

 

hangul 선택

 

OK를 클릭하고 재부팅 한다. 재부팅해도 한글 입력이 되지는 않는다.

 

Raspberry Pi Configuration - Localisation - Locale에서 언어를 한국어로 바꾸고 다시 재부팅 한다.

 

 

 

'한/영'키나 'Shift + Space'키를 누르면 한글이 입력 된다.

 

Timezone을 Asia-Seoul로 바꿔준다.

 

netstat -nr 명령어로 Gateway주소를 확인한다.

 

/etc/dhcpcd.conf 파일 아래에 고정할 ip주소를 지정한다.

 

interface eth0

static ip_address=고정할 ip주소/24

static routers=Gateway 주소

static domain_name_servers=Gateway 주소

 

(무선 랜카드를 사용 한다면 interface를 eth0이 아닌 wlan0로 바꿔 준다)

 

재부팅하고 확인해 보면 지정한 ip주소로 고정 된다.

 

Raspbian은 기본적으로 VNC Server가 설치 되어 있지만 Raspberry Pi Desktop은 VNC Server가 설치되어 있지 않다.

필요한 경우 X11VNC Server를 설치 한다.

 

 

 

X11VNC Server를 실행 한다.

 

기본 설정으로 두고 OK를 클릭 한다.

 

Accept Connections를 체크한다.

암호가 필요 없다면 그대로 두고 OK를 클릭 한다.

 

RealVNC Viewer등으로 원격 접속하여 사용 할 수 있다.

 

VNC Server를 가끔 쓴다면 위와 같이 매번 사용할 때만 실행하면 되지만 VNC Server가 항상 실행되어야 한다면 rc.local에 등록하여 부팅시 자동으로 실행 되도록 한다. /etc/rc.local 파일을 열어 준다.

 

exit 0 명령어 위에 X11VNC Server를 실행하는 명령어를 넣고 저장 한다.

 

su pi -c 'x11vnc -loop -forever'

 

아래 링크를 참고한다.

2022.05.29 - [Linux] - Linux(Ubuntu, Raspberry Pi OS) 자동실행(rc.local)

 

이제 부팅시 자동으로 X11VNC Server가 실행 된다.

 

 

 

Samba를 설치 한다.

 

/etc/samba/smb.conf 파일끝에 아래 내용을 추가 한다.

 

[share]

path = /share

public = yes

writable = yes

 

testparm 명령을 실행 해서 smb.conf 파일에 문제가 없는지 확인 한다.

 

path에 지정한 /share 디렉토리를 만들고 읽기, 쓰기, 실행이 가능하도록 rwx 권한을 준다.

 

smbd은 자동으로 실행 및 등록 된다.

 

이제 /share 디렉토리는 다른 컴퓨터에서 접근 가능 하다.

 

공유기에서 Port forwarding 설정이 필요 하다면 netstat 명령어로 필요한 port를 확인 한다.

 

컴퓨터를 켜거나 재부팅하면 항상 위와 같은 GRUB 화면이 나온다. 여기서 몇 초(5초) 기다리거나 엔터키를 한 번 눌러야 부팅이 시작된다. 바로 부팅 되도록 해보자.

 

/etc/default/grub 파일을 열자.

 

GRUB_TIMEOUT=5로 되어 있다. 그래서 5초를 기다려야 했던 것이다.

 

기다리지 않고 바로 부팅되도록 0으로 바꾸고 저장한다.

 

sudo update-grub 명령으로 업데이트 한다. 이제 기다릴 필요 없이 바로 부팅된다.

 

반응형
Posted by J-sean
:
반응형

Samba is the standard Windows interoperability suite of programs for Linux and Unix.

Since 1992, Samba has provided secure, stable and fast file and print services for all clients using the SMB/CIFS protocol, such as all versions of DOS and Windows, OS/2, Linux and many others.


This post describes how to create a public share on Samba in Fedora Linux.


Samba를 이용해 윈도우에서도 접속 가능한 리눅스 공유 폴더를 간단히 만들 수 있다. 사용자 이름이나 암호 없이 접속 가능한 간단한 공유 폴더를 만들어 보자.


Download and install Samba package.


Edit '/etc/samba/smb.conf'


Add the following in [global].

netbios name = sean

map to guest = Bad User


Make [share] at the end and add the following in it.

path = /share

public = yes

writable = yes


Run 'testparm' and check 'smb.conf'.


Move to the root directory and make a directory you want to share.

The name of the sharing directory has to be the same as the name you assigned to 'path' in 'smb.conf' [share].


※ 'share' has read and execute permissions only for 'other'. So if you access it as an 'other', you can't write to it unless you give write permission to 'other'. (chmod 777 share)



Add Samba service to the firewall and reload it.


Start and enable Samba. To start services automatically at boot, you must enable them.


Run 'File Explorer' in Windows and access to the sharing directory in Linux by its IP address.


You can also access the directory by the name you assigned to 'netbios name' in 'smb.conf' [global]


반응형
Posted by J-sean
:
반응형

Windows 10을 다운로드 하기 위해 Microsoft 소프트웨어 다운로드 페이지로 이동하면 아래와 같은 화면이 나온다.

 

미디어 생성 도구를 다운로드하고 Windows 10을 설치 할 수 있는 화면이다. Windows 10을 바로 다운로드 할 수 있는 링크는 없다.

 

F12키를 누른다(크롬). 개발자 도구 상단 왼쪽 Elements탭 옆 아이콘(Toggle device toolbar)을 클릭한다.

 

화면 상단 중간에 Responsive를 클릭하고 iPad Pro를 선택한다. 화면이 너무 작다면 적당한 크기로 확대(75%) 한다.

 

F5키를 눌러 페이지를 새로 고친다.

 

Windows 10 디스크 이미지 다운로드 페이지가 표시된다. 이 웹페이지는 현재 접속한 기기가 iPad Pro라고 생각하는 것이다. iPad에서는 미디어 생성 도구를 실행 할 수 없기 때문에 Windows 10 디스크 이미지를 바로 다운 받을 수 있게 해 준다.

 

 

버전 선택에서 Windows 10을 선택하고 확인을 클릭한다.

 

제품 언어 선택에서 한국어를 선택하고 확인을 클릭한다.

 

원하는 bit를 선택 한다.

 

다운로드가 진행 된다.

 

반응형
Posted by J-sean
:
반응형

PC가 아닌 환경에서 작동하는 프로그램을 만든다는건 내게 낮설게만 느껴지는 일이었다. 일반적으로 사용하지 않는, 특별한 용도의 장비(항공, 우주, 의료, 군사 분야 등등..)에나 필요한 전문분야로 생각했기 때문이다. 하지만 스마트폰의 인기와 함께 널리 보급된 안드로이드, iOS는 프로그래머도 아닌 내게 새로운 도전거리(골치거리)를 선물해 주었다. 스마트폰용 앱을 개발해 보는 일이 그것이었다.

 

RTOS중 하나인 LynxOS 로고

 

재미있게도 전부터 '이게 왜 없을까? 정말 없다면 내가 만들면 좋을거 같은데..' 하고 생각 했던 앱이 있긴 했다. 내비게이션을 사용하며 운전하다 보면 목적지에 도착하기 10~15분쯤 전 상대방에게 미리 연락을 하고 싶어진다. 지금 어디쯤 왔으니 한 10분 후에 도착 할거 같다고. 정말 없는건지 나만 못찾는건지는 모르겠지만 어쨌든 보이지 않으니 내가 만들면 되는거 아닌가? (이 앱을 '다왔어'라고 부르기로 하자)

 

기기야 셀 수 없이 많지만 크게 두 가지로 나눌 수 있는 OS의 선택은 굉장히 쉬웠다. 안드로이드. 애플 제품을 좋아하지 않는 나의 유일한 선택지였다. 바로 인터넷에서 안드로이드 관련 내용을 찾아 읽어 보면서 무엇을 어떻게 공부해야 할지 생각했다. 그리고 몇 주간 안드로이드 개발 관련 서적을 공부하며 필요한 내용을 머릿속에 정리하기 시작 했다. 내 위치에서 상대방까지의 거리를 계산하기 위한 GPS기능, 메세지를 보내기 위한 SMS기능, 스마트폰에서 상대방 전화 번호 가져오기 기능 등등.

 

 

공부하기 전에는 알지 못했던 여러가지 권한 주기라든가 안드로이드 버전에 따라 없어지거나 변경된 기능들, 에뮬레이터에서는 되지만 실제 기기에서는 제대로 작동 하지 않는 내용들을 하나 하나 정리해 가며 천천히 준비 했다. 역시 무엇보다도 프로그래밍과는 무관한 회사를 다니며 앱 개발을 준비한다는건 시간적으로나 체력적으로도 쉽지 않은 일이었다. 하지만 시간이 많으면 오히려 정신적으로 나태해지기 쉽다는걸 알기 때문에 내 상황에 불만을 가지지는 않았다.

 

어쨌든 공부해서 대충 완성은 했고.. 간단히 다왔어의 코드를 살펴 보자.

 

1
2
3
4
    <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
    <uses-permission android:name="android.permission.READ_CONTACTS"/>
    <uses-permission android:name="android.permission.WRITE_CONTACTS"/>
    <uses-permission android:name="android.permission.SEND_SMS"/>
 

우선 다왔어 실행에 필요한 권한을 요청하기 위한 준비를 해 준다. 요청할 이름에서도 알 수 있다시피 위치, 연락처, 그리고 SMS 권한이다.

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
public void checkPermissions(String[] permissionRequests) {
    final ArrayList<String> permissionRequestList = new ArrayList<String>();
 
    for (final String request : permissionRequests) {
        if (ContextCompat.checkSelfPermission(this, request) != PackageManager.PERMISSION_GRANTED) {
            if (ActivityCompat.shouldShowRequestPermissionRationale(this, request)) {   // Redundant in this case
                permissionRequestList.add(request);
            } else {
                permissionRequestList.add(request);
            }
        }
    }
 
    if (!permissionRequestList.isEmpty()) {
        final String[] results = new String[permissionRequestList.size()];
        permissionRequestList.toArray(results);
 
        AlertDialog.Builder builder = new AlertDialog.Builder(this);
        builder.setTitle("info");
 
        String msg = "This app won't work properly unless you grant below permissions.";
        for (String str : results)
            msg += ("\n- "+ str);
 
        builder.setMessage(msg);
        builder.setIcon(android.R.drawable.ic_dialog_info);
 
        builder.setNeutralButton("OK"new DialogInterface.OnClickListener() {
            @Override
            public void onClick(DialogInterface dialog, int which) {
                ActivityCompat.requestPermissions(MainActivity.this, results, MY_PERMISSION_REQUESTS);
            }
        });
 
        AlertDialog dialog = builder.create();
        dialog.show();
    }
}
 
 

앱 시작시 권한을 요청할 함수이다. 여러가지 권한을 한 번에 요청 할 수 있도록 ArrayList를 사용한다.

 

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
findButton.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View v) {
        String search = targetText.getText().toString();
        List<Address> addressList;
        try {
            addressList = geocoder.getFromLocationName(search, 10);
            String[] split = addressList.get(0).toString().split(",");
            String address = split[0].substring(split[0].indexOf("\""+ 1split[0].length() - 2);
            String latitude = split[10].substring(split[10].indexOf("="+ 1);  // 위도(수평선)
            String longitude = split[12].substring(split[12].indexOf("="+ 1); // 경도(수직선)
 
            String data = "목적지: " + address;
            targetView.setText(data);
            targetLocation.setLatitude(Double.parseDouble(latitude));
            targetLocation.setLongitude(Double.parseDouble(longitude));
 
            float[] distance = new float[1];
            Location.distanceBetween(myLocation.getLatitude(), myLocation.getLongitude(), targetLocation.getLatitude(), targetLocation.getLongitude(), distance);
            distanceView.setText("현재 위치와의 거리: " + distance[0/ 1000.0 + "km");
 
            isStarted = true;
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
});
 
 

내 위치와 목적지를 파악해 거리를 계산하는 함수이다. 구글 Geocoder를 사용하기 때문에 이 함수 사용시에는 인터넷 연결이 필요하다.

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public void getContacts(String id) {
    Cursor cursor;
 
    try {
        cursor = getContentResolver().query(ContactsContract.CommonDataKinds.Phone.CONTENT_URI,
                null,
                ContactsContract.CommonDataKinds.Phone.CONTACT_ID + "=?",
                new String[] {id},
                null);
 
        if (cursor.moveToFirst()) {
            recipientName = cursor.getString(cursor.getColumnIndex(ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME));
            recipientNumber = cursor.getString(cursor.getColumnIndex(ContactsContract.CommonDataKinds.Phone.NUMBER));
            recipientView.setText("받는 사람: " + recipientName + '\n' + "전화번호: " + recipientNumber + '\n');
 
            cursor.close();
        }
    } catch (Exception e) {
        e.printStackTrace();
    }
}
 
 

스마트폰의 전화번호부를 검색해 이름과 전화번호를 가져오는 함수이다. 공부했던 책의 내용이 에뮬레이터에서는 정상 작동 하지만 실제 기기에서 제대로 작동하지 않아 버그 잡는데 1시간이 넘게 걸렸다.

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
public void onLocationChanged(Location location) {
    if (isStarted) {
        double latitude = location.getLatitude();
        double longitude = location.getLongitude();
 
        myLocation.setLatitude(latitude);
        myLocation.setLongitude(longitude);
 
        float[] distance = new float[1];
        Location.distanceBetween(myLocation.getLatitude(), myLocation.getLongitude(), targetLocation.getLatitude(), targetLocation.getLongitude(), distance);
        distanceView.setText("현재 위치와의 거리: " + distance[0/ 1000.0 + "km");
 
        if (distance[0/ 1000.0f < Float.parseFloat(distanceText.getText().toString())){
            try {
                SmsManager smsManager = SmsManager.getDefault();
                smsManager.sendTextMessage(recipientNumber, null, smsText.getText().toString(), nullnull);
                // 문자 전송 알림음
                Uri uri = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION);
                Ringtone ringtone = RingtoneManager.getRingtone(getApplicationContext(), uri);
                ringtone.play();
 
                Toast.makeText(getApplicationContext(), "메세지를 보냈습니다. 앱을 종료 합니다.", Toast.LENGTH_SHORT).show();
            } catch (Exception e) {
                Toast.makeText(getApplicationContext(), e.getMessage(), Toast.LENGTH_SHORT).show();
            }
 
            //  프로그램 종료하기
            isStarted = false;
 
            finish();
        }
    }
}
 
 

내 위치가 바뀔때마다 목적지와의 거리를 다시 계산하고 출력해 주는 함수이다. 지정한 거리 내로 들어오면 메세지를 보내고 앱을 종료한다. 에뮬레이터에서는 이 함수가 3초 정도에 한 번씩 자동 호출 되지만 기기에서는 실제 위치가 바뀔때만 호출되는 차이가 있다는걸 알지 못해 버그 잡는데 시간이 많이 걸렸다.

 

 

단순한 내용의 앱이기 때문에 추가 Fragment나 Activity 없이 단 한 개의 Activity만으로 디자인 했다.

 

 

첫 실행 화면

 

내 스마트폰에 설치하고 직접 테스트하는 영상. 물론 결과는 성공.

 

실제 사용해 보면 알게 되지만 이 앱은 몇 가지 단점이 있다.

 

실제 이동 거리를 계산하는 일반적인 거리 개념이다. 

 

다왔어의 거리 계산 방법. 내 현재 위치와 목적지의 직선 거리를 계산 한다.

 

구글 맵이나 내비게이션 API를 사용하면 실제 이동 거리로 거리 계산을할 수 있을거 같다. 또, 일반적으로 일정 시간이 지나면 스마트폰의 화면이 꺼지게 되는데 다왔어는 화면이 꺼지는 경우 더 이상 작동하지 않는다. 화면을 켜면 다시 거리 계산을 시작하고 작동 한다. 이 점을 방지하기 위해 자동으로 화면이 꺼지지 않도록 했는데 사용자가 전원 버튼을 눌러 화면을 끄거나 다른 앱으로 전환하는 경우 다시 다왔어로 돌아 오지 않는다면 결국 작동 하지 않는다.

 

사실 그 동안 공부한 내용을 빨리 적용해 보고 싶은 마음에 완성도 높은 앱으로 마무리 하지는 못했다. 알고 싶은게 있으면 알 때까지 공부하지만 알게되면 흥미가 좀 떨어져버리는 내 성격 때문인거 같다. 나중에 필요하다면 좀 더 보완해서 완성도를 높이겠지만 그게 언제가 될지는 모르겠다.

 

그 전에 궁금한 사람이 있다면 직접 설치해서 사용해 볼 수 있도록 apk 파일은 아래에 공유한다.

almost.apk
다운로드

 

반응형
Posted by J-sean
:
반응형

Each vertex of a polygon, separate triangle, or separate quadrilateral specified between a glBegin/glEnd pair is marked as the start of either a boundary or nonboundary edge. If the current edge flag is TRUE when the vertex is specified, the vertex is marked as the start of a boundary edge. If the current edge flag is FALSE, the vertex is marked as the start of a nonboundary edge.


OpenGL에서 선을 그릴때 다각형의 경계선인지 숨겨야할 내부선인지 결정 할 수 있다.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
#include <gl/glut.h>
 
void DoDisplay();
 
int main(int argc, char** argv)
{
    glutInit(&argc, argv);
    glutCreateWindow("OpenGL");
    glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
    glutDisplayFunc(DoDisplay);
    glutMainLoop();
 
    return 0;
}
 
void DoDisplay()
 
{
    glClear(GL_COLOR_BUFFER_BIT);
 
    glPushMatrix();
 
    glTranslatef(-0.5f, 0.0f, 0.0f);
 
    // 첫 번째 선 Nonboundary 지정
    glBegin(GL_TRIANGLES);
    glEdgeFlag(GL_FALSE);   // 경계선 적용 여부를 선택할 선의 시작점 선언 전에 glEdgeFlag()를 호출.
    // Flags edges as either boundary or nonboundary.
    glVertex2f(0.0f, 0.2f);
    glEdgeFlag(GL_TRUE);
    glVertex2f(-0.2f, -0.2f);
    glVertex2f(0.2f, -0.2f);
    glEnd();
 
    glTranslatef(0.5f, 0.0f, 0.0f);
 
    // 두 번째 선 Nonboundary 지정
    glBegin(GL_TRIANGLES);
    glVertex2f(0.0f, 0.2f);
    glEdgeFlag(GL_FALSE);
    glVertex2f(-0.2f, -0.2f);
    glEdgeFlag(GL_TRUE);
    glVertex2f(0.2f, -0.2f);
    glEnd();
 
    glTranslatef(0.5f, 0.0f, 0.0f);
 
    // 세 번째 선 Nonboundary 지정
    glBegin(GL_TRIANGLES);
    glVertex2f(0.0f, 0.2f);
    glVertex2f(-0.2f, -0.2f);
    glEdgeFlag(GL_FALSE);
    glVertex2f(0.2f, -0.2f);
    glEdgeFlag(GL_TRUE);    
    glEnd();
 
    glPopMatrix();
 
    glFlush();
}



Run the program.


반응형
Posted by J-sean
:
반응형

인터넷 서점이 없던 어린 시절엔 동네 책방에서 책을 구입하곤 했다. 주로 주택가에 살았기 때문에 집 근처에는 없었고 30분정도 걸어 가야 번화가에서 서점을 찾을 수 있었다. 학교를 다니던 어린 시절이었으니 주로 구입했던 책은 문제집이나 학습서였지만 그 보다 자주, 한 달에 한번씩은 컴퓨터 관련 잡지를 샀던거 같다. 사실 그 때 컴퓨터 잡지를 얼마나 이해 하고 봤던건지는 잘 모르겠다. 아마 게임에 관련된 부분만 열심히 보고 나머지는 별다른 '이해'없이 '읽기'라는 활동에만 집중했던게 아닐까 싶다.


이젠 인터넷에서도 깨끗한 책 사진을 찾기 어려운 '컴퓨터 랜드'


PC통신과 함께 컴퓨터 보급에 앞장섰던 컴퓨터 잡지 'PC Line'


물론 그런 책들만 샀던건 아니다. 언젠가는 클래식이라 불릴만한 고전 문학에 관심이 생겨 영미 문학이나 한국 문학책을 구입하기도 했고 그리스 로마 신화에 관한 책, 영어에 관한 책들도 꽤 많이 구입했다 . 지금도 책장 한 구석에는 아직 읽지 않은 많은 문학책이 꽂혀 있다.


혼자 공부하는 방법은 여러가지가 있겠지만 난 주로 책을 통해 배우는걸 좋아한다. 어떤 특정 분야에 관심이 생기면 우선 관련 책부터 알아보고 괜찮아 보이는 책을 여러권 구매 한다. 서점에서 훑어 보고 산다고 해도 나중에 시간을 들여 자세히 읽다보면 기대에 못 미치는 경우도 많고 서로 다른 책들의 장점을 내 나름대로 조합해 나가야 하는 경우가 많기 때문이다. 덕분에 예전엔 제값을 못하는 책을 사게 되기도 했지만 많은 경험이 쌓이다 보니 이젠 책 선택에 실패하는 경우는 극히 들물어졌다.


한때 언어, 반도체, 디스플레이, 설계등 여러 분야에 관심을 가지기도 했지만 아직까지도 나의 가장 큰 관심 분야는 컴퓨터이다. 하지만 이젠 멀리 서점까지 가서 구매하지는 않는다. 가끔 서점에 가서 책을 훑어보긴 하지만 결국 구매는 인터넷 서점에서 한다. 종종 벌어지는 카드 할인 이벤트나 적립금의 유혹을 뿌리치기가 쉽지 않기 때문이다.


인터넷 서점에서 확인한 2019년 도서 구매 금액과 치킨과의 비교


몇 년 전부터 자주 이용하는 인터넷 서점에서 매년 말이 되면 얼마나 책을 샀는지, 어떤 책들을 샀는지 기록을 보여주고 있다. 내 기억이 맞다면 거의 매년 약 100만원 정도의 책을 샀던거 같다. 상위 1%정도라고 하니 꽤 많은 책들을 사고 있는거 같다. 하긴 이젠 내 방에 책 둘 공간이 없어 어떻게 해야 하나 고민이 될 정도이니 적진 않은거 같다.


책 구입이 수집인냥 방에 쌓여가는 책들을 보면 기분이 좋을때도 있지만 한편으론 씁쓸하기도 하다. 저 많은 책들 중 내가 정말 읽었다고 할 만한 책은 몇 권이나 될까? 책값도 많이 올라 이젠 5만원짜리 책도 흔해 지는 요즘, 지난 번 산 책은 다 읽었나? 이번에 또 책을 사면서 돈을 버리는건 아닐까 하는 걱정.. 무엇보다 너무 기술 서적에만 편중된 독서를 하는거 같아 걱정이다. 사실 기술 서적을 읽는 행위가 독서에 포함되는지도 의문이다.


그러거나 말거나 어제 또 구매한 '궁국의 기술' 서적


이젠 살만큼 산거 같은데 괜찮은 책을 발견하면 또 다시 장바구니를 채우는 나를 발견 하곤 한다. 이 책을 사지 않으면 남보다 뒤쳐지는 느낌이랄까? 남들만큼 원하는 공부를 하지 못해 불안한 마음을 책 구매로 해소하는지도 모르겠다. 아마 내년이면 상황이 많이 달라질테니 그런 마음이 조금은 줄어들기를 바래 본다.


반응형
Posted by J-sean
:
반응형

It describes how to draw the simple solar system that is composed of the Sun, the Earth and the Moon.

OpenGL로 태양, 지구, 달로 구성된 간단한 태양계 그리기


1 day = 10 frames

1 month = 30 days (300 frames)

1 year = 12 months (3,600 frames)


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
#include <gl/glut.h>
#include <iostream>
 
GLfloat EarthRotation = 0.0f;
GLfloat MoonRevolution = 0.0f;
GLfloat EarthRevolution = 0.0f;
 
GLfloat FramePerYear = 3600.0f;
GLint delay = 10;
 
GLint Year = 2020;
GLint Month = 1;
GLint Day = 1;
 
void MyDisplay();
void MyTimer(int value);
 
int main(int argc, char** argv)
{
    glutInit(&argc, argv);
    glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB);
    glutCreateWindow("OpenGL Solar System");
 
    gluLookAt(
        0.01.00.0,    // Eye
        0.00.00.0,    // Center
        1.00.01.0    // Up
    );
    // gluLookAt creates a viewing matrix derived from an eye point, a reference point indicating
    // the center of the scene, and an UP vector.
 
    glutTimerFunc(delay, MyTimer, 1);
    glutDisplayFunc(MyDisplay);
    
    glutMainLoop();
 
    return 0;
}
 
void MyDisplay()
{
    glClear(GL_COLOR_BUFFER_BIT);
    
    // The Sun
    glutWireSphere(0.44020);
    
    // The Earth
    glPushMatrix();
    glRotatef(EarthRevolution, 0.0f1.0f0.0f); // Earth's revolution
    glTranslatef(0.7f0.0f0.0f);
    
    glRotatef(-EarthRevolution, 0.0f1.0f0.0f);    // Earth의 Revolution에 의한 Earth의 회전 삭제.
    glRotatef(EarthRotation, 0.0f1.0f0.0f);    // Earth's rotation
    glutWireSphere(0.12010);
 
        // The Moon
        glPushMatrix();
        glRotatef(-EarthRotation, 0.0f1.0f0.0f);    // Earth의 Rotation에 의한 Moon의 회전 삭제.
        glRotatef(MoonRevolution, 0.0f1.0f0.0f); // Moon's revolution
        glTranslatef(0.2f0.0f0.0f);
        glutWireSphere(0.03105);
        glPopMatrix();
 
    glPopMatrix();
    
    glutSwapBuffers();
}
 
void MyTimer(int value)
{
    EarthRotation += (360.0f * 360.0f/ FramePerYear;    // 지구 자전: (360도 * 360일) / FramePerYear(3600) = 36도
    if (EarthRotation >= 360.0f) {
        EarthRotation = 0.0f;
 
        std::cout << Year << "-" << Month << "-" << Day << std::endl;
        // 날짜 출력 코드가 계산 코드(Day++, Month++, Year++)보다 먼저 처리 되야 제대로 표시 된다.
 
        Day++;
        if (Day > 30)
            Day = 1;
    }
 
    MoonRevolution += (360.0f * 12.0f/ FramePerYear;
    // 달의 공전 주기는 약27일 이지만 30일(1달)로 설정.
    // Rotation은 Revolution 과 같은 주기로 따로 계산할 필요 없음.
    // Revolution 처리 만으로 항상 달의 같은면이 지구를 바라 봄.
    if (MoonRevolution >= 360.0f) {
        MoonRevolution = 0.0f;
 
        Month++;
        if (Month > 12)
            Month = 1;
    }
 
    EarthRevolution += 360.0f / FramePerYear;    // 1 프레임에 0.1도 회전. 3600 프레임이 1년.
    if (EarthRevolution >= 360.0f) {
        EarthRevolution = 0.0f;
 
        Year++;
    }
    
    glutPostRedisplay();
    glutTimerFunc(delay, MyTimer, 1);
}



Run the program and see how it works.


반응형
Posted by J-sean
: