directory 정보 관련 structure

struct dirent {
	/* Always zero */
	long d_ino;

	/* Position of next file in a directory stream */
	long d_off;

	/* Structure size */
	unsigned short d_reclen;

	/* Length of name without \0 */
	size_t d_namlen;

	/* File type */
	int d_type;

	/* File name */
	char d_name[PATH_MAX+1];
};
typedef struct dirent dirent;
struct DIR {
	struct dirent ent;
	struct _WDIR *wdirp;
};
typedef struct DIR DIR;

 

주로 사용되는 systemcall prototype

static DIR *opendir(const char *dirname);

static struct dirent *readdir(DIR *dirp);

static int closedir(DIR *dirp);

static void rewinddir(DIR *dirp);
//directory라는 파일을 읽기 위해 사용하는 파일 포인터를 맨 앞으로 이동

 

 

 

GitHub - tronkko/dirent: C/C++ library for retrieving information on files and directories

C/C++ library for retrieving information on files and directories - GitHub - tronkko/dirent: C/C++ library for retrieving information on files and directories

github.com

 

몇 주 전 시작한 삽질이 이제까지 계속될 줄은 몰랐다.
처음에는 macOS에 올리려고 거의 몇 주 내내 삽질을 했는데 도저히 안돼서 ubuntu로 돌렸다.
그래도 macOS에서 삽질하면서 많이 배워서인지 ubuntu에서는 금방 해냈다.!
(mac에서 성공하신 분들 정말 대단... b)

감격스러움에 눈물이 앞을 가리지만.. 잊어버리기 전에 얼른 적어둬야지


개발 환경

Ubuntu 18.04 LTS
Nginx 1.14.0
Django 2.2.4

 

  • 이 포스트는 이미 어느 정도 만들어져 개발 서버로는 동작이 되는 Django 프로젝트가 존재한다는 상황을 가정한다.
  • virtualenv를 이용한 가상 환경에서의 작업을 전제로 한다.

 

여기서 사용할 Django + uWSGI + Nginx 간의 관계는 다음과 같다.

Client <-> Nginx <-> uWSGI <-> Django

: Nginx가 Client들로부터 비동기식으로 요청을 받아 이를 uWSGI한테 넘겨주면, uWSGI는 이를 Django가 이해할 수 있는 형태로 바꿔 Django에게 요청을 넘겨주고 그에 대한 응답이 다시 위 흐름대로 Client에게 돌아가는 방식

이러한 구조를 통해 내가 생각한 구현 흐름은 이렇다.
Django로 서비스를 만들고 이를 uWSGI와 연결하고 실행하면 특정 포트로 이 웹서버에 접근할 수 있을 것이다. (socket 설정 전)
그 후 Nginx와는 소켓을 이용하여 통신하도록 할 것이기 때문에 uwsgi와 nginx 사이에서는 소켓 파일을 공유하도록 하면 된다.

이를 바탕으로 작업한 내용은 다음과 같다.

 

현재 포트 포워드 상태

1. 외부 :8880 -> 내부 :8080
2. 외부 :8888 -> 내부: 8000

최종 연결은 1번 포트에 할 건데,
중간에 테스트용으로 1번과 2번을 암거나 가져다 쓸 수 있으니 양해 부탁..

 

Django

1) Directory 구조

repo
    - db.sqlite3
    - manage.py
    - app
        - migrations
        - static
        - templates
        - __init__.py
        - admin.py
        - apps.py
        - models.py
        - urls.py
        - views.py
    - conf
        - __init__.py
        - asgi.py
        - settings.py
        - urls.py
        - wsgi.py

2) settings.py

ALLOWED_HOSTS = ['*']
STATIC_URL = '/static/'

 

여기까지 한 것을 아래 명령어를 이용하여 실행 후 접속하면 다음과 같이 원하던 대로 잘 접속되는 것을 볼 수 있다.

$ python3 manage.py runserver 0:8080

 

이 템플릿 어디서 가져왔는데 기억이 안난다... Thank you so much for making good design template

 

uWSGI

1) /etc/uwsgi/django.ini

[uwsgi]
home = /path/to/venvFolder/venv
chdir = /path/to/project/repo
module = conf.wsgi:application

http = :8000

master = True
processes = 5

logto = /var/log/uwsgi/uwsgi.log

** Nginx와 연결하기 전에 uwsgi와 django가 연결이 되고 있는지를 확인해보기 위해 우선 http 방식으로 설정하였다.
Nginx와 연결할 때에는 socket으로 바꿀 예정

아래의 명령어로 uwsgi를 실행시킨 후 8000번 내부 포트에 대응하는 8888번 외부 포트로 접속하여 진행상황을 확인한다.

$ uwsgi --ini /etc/uwsgi/django.ini
[uWSGI] getting INI configuration from /etc/uwsgi/django.ini
*** Starting uWSGI 2.0.19.1 (64bit) on [Sat Sep 11 05:16:26 2021] ***
compiled with version: 7.5.0 on 11 September 2021 00:27:30
os: Linux-4.4.0-1128-aws #142-Ubuntu SMP Fri Apr 16 12:42:33 UTC 2021
nodename: goorm
machine: x86_64
clock source: unix
pcre jit disabled
detected number of CPU cores: 2
current working directory: /home
detected binary path: /usr/local/bin/uwsgi
chdir() to /home/ssg
your memory page size is 4096 bytes
detected max file descriptor number: 1024
lock engine: pthread robust mutexes
thunder lock: disabled (you can enable it with --thunder-lock)
uwsgi socket 0 bound to UNIX address /path/to/socketFile/uwsgi.sock fd 3
setuid() to 33
Python version: 3.7.4 (default, Nov  4 2020, 10:17:35)  [GCC 7.4.0]
PEP 405 virtualenv detected: /path/to/venvFolder
Set PythonHome to /path/to/venvFolder
*** Python threads support is disabled. You can enable it with --enable-threads ***
Python main interpreter initialized at 0x55eb1a8d8020
your server socket listen backlog is limited to 100 connections
your mercy for graceful operations on workers is 60 seconds
mapped 437520 bytes (427 KB) for 5 cores
*** Operational MODE: preforking ***
WSGI app 0 (mountpoint='') ready in 2 seconds on interpreter 0x55eb1a8d8020 pid: 528 (default app)
*** uWSGI is running in multiple interpreter mode ***
spawned uWSGI master process (pid: 528)
spawned uWSGI worker 1 (pid: 531, cores: 1)
spawned uWSGI worker 2 (pid: 532, cores: 1)
spawned uWSGI worker 3 (pid: 533, cores: 1)
spawned uWSGI worker 4 (pid: 534, cores: 1)
spawned uWSGI worker 5 (pid: 535, cores: 1)

 

django 프로젝트는 잘 찾는 것 같은데, 왜인지 static 파일을 하나도 찾지 못하고 있는 것 같다. 일단 django 프로젝트만이라도 Nginx까지 연결해보자 싶어서 계속 진행했다.

Nginx와 연결할 때에는 socket을 이용하고자 아래와 같이 django.ini를 수정하였다.

[uwsgi]
base = /.../WEB
home = %(base)/venv
chdir = %(base)/repo
module = conf.wsgi:application

socket = %(base)/run/uwsgi.sock
chown-socket = www-data:www-data;
chmod-socket = 660
vacuum = True

master = True
processes = 5

logto = /var/log/uwsgi/uwsgi.log

여기서 chown과 chmod 설정을 해주지 않으면 추후에 client의 요청이 nginx를 통해 처리될 때 권한 문제로 .sock파일을 사용하지 못하는 에러가 발생한다. www-data는 nginx 설정에 적힌 user 이름이다. (뭘로 하든 nginx user와 같은 사용자로 파일 권한 설정해야 함)

(nginx와 uwsgi가 .sock파일을 이용하여 통신할 것이고 이 때 .sock파일이 매개가 되는 상황.)

 

Nginx

1) /etc/nginx/nginx.conf

user www-data;
http {
	include	/etc/nginx/sites-enabled/*;
    include	mime.types;
    default_type application/octet-stream;    
    ...
}

앞서 나왔던 대로 user를 www-data로 한다. (근데 이건 사실 default로 적혀있을 거라서 그냥 내버려 두면 된다.)
include로 sites-enabled 폴더 내용 가져오는 것도 기본적으로 적혀있을 내용이긴 하다. 별 차이 없으면 패스
(+여기서 삽질했던 거: nginx.conf 파일 안에는 server 블록이 필요 없다. server 블록은 sites-available/project.conf에 적으면 됨...)

2) /etc/nginx/sites-available/django.conf

upstream django {
	server unix:/path/to/socketFolder/uwsgi.sock;
}

server {
	listen 8080;
    server_name [local ip addr];
    
    charset utf-8;
    root /path/to/project/repo;
    
    location / {
    	include /etc/nginx/uwsgi_params;
        uwsgi_pass django;
    }
    
    location /static/ {
    	alias /path/to/project/app/static/;
    }
}

(+ static 경로 마지막에 / 를 꼭 붙여주자)

sites-available에 django.conf 파일을 작성했으면 이를 sites-enabled 폴더에 링크 걸어주는 것까지 해야 한다.

$ sudo ln -sf /etc/nginx/sites-available/django.conf  /etc/nginx/sites-enabled/

여기까지 한 뒤 uwsgi와 nginx를 실행시킨다.

$ service nginx start
$ uwsgi --ini /etc/uwsgi/django.ini

/etc/nginx/sites-available/django.conf 에서 port를 8080번으로 설정했기 때문에 이에 대응하는 8880번 포트로 접속하면 static 파일까지 다시 성공적으로 나온다!


참고

 

Python에서 실행 중이던 프로그램을 종료시키는 방법에는 아래 4가지가 있다.

1. quit()
2. exit()
3. sys.exit([arg])
4. os._exit([arg])


1. quit() 를 사용하는 방법

quit() 메소드를 이용하여 종료 작업을 수행할 경우 quit() 는 실행 중이던 프로그램뿐만 아니라 python 쉘까지 완전히 닫아버린다.
이러한 이유로 quit()는 python 쉘에서만 사용해야하고 프로그램 내에서는 사용하지 않는 것을 권장하고 있다.

 

2. exit() 를 사용하는 방법

exit() 메소드 역시 python 프로그램을 종료시킬 수 있는 메소드이지만 quit() 와 비슷하게 실행 중이던 프로그램뿐만 아니라 python 프로세스까지 완전히 종료시킨다. 따라서 python 쉘에서 사용하도록 하고 프로그램 내에서는 사용하지 않을 것을 권장한다.

 

3. sys.exit([arg]) 를 사용하는 방법

sys.exit()는 import sys 를 필요로 한다.
quit()나 exit()와는 달리, 쉘까지 날리지는 않으면서 프로그램만 정상적으로 종료시킬 수 있다.
특히 sys.exit() 메소드는 인자를 받는데, 0의 경우 정상적인 종료를 의미한다.
이러한 이유로 sys.exit()는 프로그램 내에서의 종료 동작을 수행하기 위한 가장 적합한 방법으로 소개된다.

sys.exit()의 인자로 0 이외의 다른 값을 사용할 경우 비정상적인 종료를 의미하는데,
아무런 인자도 사용하지 않을 경우에는 정상적인 종료(default: 0)를 하는 것으로 여겨진다.
0 이외의 값도 사용할 수 있다고는 하지만 대부분의 시스템에서는 이 값으로 0-127 범위의 정수를 사용하도록 한다.

인자로 정수가 아닌 오류 메세지를 입력할 경우 이를 출력하면서 종료시킬 수 있어, 예외 처리를 하는 데에도 유용하게 쓰일 수 있을 것 같다.

정상적인 종료를 할 경우
메세지를 출력하도록 하는 경우

 

4. os._exit([arg]) 를 사용하는 방법

os._exit()는 import os를 필요로 한다.
sys.exit()와 같이 프로그램만을 종료할 수 있도록 하지만, os._exit()에서는 인자로 상태 코드가 꼭 필요하다.
(python.org에서는 인자가 필수가 아니라고 하지만, 테스트해보았을 때에는 인자없이 실행할 경우 에러가 발생하였다..)
정상적인 종료를 의미할 경우 os.EX_OK 를 인자로 넘겨주면 된다.
이것 외의 다른 상태 코드는 아래 링크에서 확인할 수 있다.

 

os — Miscellaneous operating system interfaces — Python 3.9.5 documentation

os — Miscellaneous operating system interfaces Source code: Lib/os.py This module provides a portable way of using operating system dependent functionality. If you just want to read or write a file see open(), if you want to manipulate paths, see the os.

docs.python.org

 

정상적인 종료를 할 경우

 

1. 리스트

C언어에서의 배열과 비슷하며, 같은 형태의 여러 자료를 함께 관리하기에 좋다.

다만 배열과의 차이점이라고 할 수 있는 것은,

배열은 하나의 배열에 저장되어 있는 데이터의 자료형이 모두 같아야 하지만,

리스트에서는 서로 다른 자료형의 데이터를 하나의 리스트에 저장할 수 있다. (그러나 대게 같은 자료형의 데이터를 저장함)

 

1) 선언 및 초기화

 

빈 리스트를 선언하고자 할 때에는 다음과 같이 한다.

# 빈 리스트 선언
l = []

 

리스트를 선언과 함께 초기화까지 하고 싶은 경우 다음과 같이 작성할 수 있다.

l = [0]

 

 

이렇게 할 경우 리스트의 원소 자리 하나만을 0으로 초기화하는 것이 된다.

현재 l의 길이는 1이 된다.

그렇다면 리스트의 크기를 3으로 하면서 0으로 초기화하고 싶은 경우에는 다음과 같이 작성할 수 있을 것이다.

l = [0, 0, 0]

 

그렇다면 리스트의 크기를 100으로 하고 싶을 때에는 0을 100번 써주어야 하는 것일까?

이 경우에는 다음의 방법을 이용해 좀 더 편리하게 초기화를 할 수 있다.

l = [0] * 100

 

 

2) 원소 접근 및 수정

C언어의 배열과 같이 Python의 리스트에서도 리스트의 첫 원소의 인덱스 번호는 0부터 시작한다.

그렇기 때문에 제일 첫 번째 원소에 접근하고 싶은 경우에는 다음과 같이 0에 접근해야 한다.

l = [0, 2, 3]

print(l[0]) # 0
print(l[2]) # 3

l[0] = 1 # l = [1, 2, 3]
print(l[0]) # 1

 

 

3) 원소 추가

이미 선언된 리스트에 새로운 원소를 추가하고 싶은 경우 다음의 방법을 사용할 수 있다.

l.append(4) # l = [1, 2, 3, 4]

 

 

리스트의 중간에 원소를 추가하고 싶은 경우 다음의 방법을 사용한다.

l.insert(1, 5) # l = [1, 5, 2, 3, 4]

insert 함수의 첫 번째 인자로 추가할 원소가 갖게 될 인덱스를 입력하고, 두 번째 인자로 추가할 원소의 값을 입력하면 된다.

 

 

4) 원소 삭제

리스트의 값을 기준으로 원소를 삭제하고 싶은 경우

만일, 리스트에 원하는 값이 중복될 경우 인덱스 번호가 가장 낮은 위치에 있는 원소를 삭제한다.

l.remove(2) # l = [1, 5, 3, 4]

 

리스트의 인덱스를 기준으로 원소를 삭제하고 싶은 경우

만일, 인덱스를 지정해주지 않으면 리스트의 가장 마지막 원소를 삭제하고 반환한다.

e = l.pop(1) # l = [1, 3, 4]
print(e) # 5

* pop() 말고 del 키워드를 이용하는 방법도 있다. 이 경우에는 삭제되는 원소를 반환하지 않는다.

del l[2] # l = [1, 3]

 

리스트의 모든 원소를 삭제하고 싶은 경우

l.clear() # l = []
# 또는
del l[:]

 


 

2. 튜플

튜플은 리스트와 비슷하지만 변경할 수 없어, 참조형 리스트를 만들 때 사용된다.

 

1) 초기화

리스트는 대괄호[]를 사용했다면 튜플은 소괄호()를 사용한다.

또한 튜플은 단순히 여러 데이터를 콤마로 연결한 것으로 초기화를 할 수도 있다.

t1 = (1, 2, 3) # type(t1) : tuple
t2 = 1, 2, 3 # type(t2) : tuple

 

 

2) 튜플의 데이터가 변경될 수 있는 경우

튜플 자료형 자체는 원래 그 안의 데이터를 변경할 수 없게 되어있다.

그러나 튜플 안의 데이터가 리스트라면?

튜플에 또 다른 데이터를 추가하거나 수정하는 것은 안되지만, 튜플 내의 리스트를 수정하는 것은 가능하다.

t = (1, [1, 2, 3])

t[0] = 2 ## 사용 불가능한 코드

t[1][0] = 0 # t = (1, [0, 2, 3])
t[1].append(4) # t = (1, [0, 2, 3, 4])
t[1].clear() # t = (1, [])

 


 

3. 집합

집합은 중복된 요소가 없는 리스트라고 생각하면 된다.

중괄호{}를 사용하고, 집합을 사용하면 합집합, 교집합, 차집합과 같은 집합 연산이 가능하다.

따라서 대게 리스트의 중복 요소를 제거하거나 집합 연산이 필요할 때 사용된다.

 

1) 선언 및 초기화

여기서 주의해야할 점은 빈 집합을 선언하려고 할 때 s = {} 라고 할 경우, 빈 집합이 아닌 빈 딕셔너리가 선언된다.

또한 집합 초기화 시 중복된 원소를 입력하면 집합의 원소에는 입력된 값 중 중복된 원소를 제거한 나머지 원소들만 저장이 된다.

# 빈 집합 선언
s = set()

# 집합 초기화
s = { 1, 2, 3, 2, 3, 4 }

print(s) # { 1, 2, 3, 4 }

 

 

2) 원소 추가 및 삭제

# add(): 추가
>>> a.add(5)
>>> a
{1, 2, 3, 4, 5}


# remove(): 삭제
>>> b.remove(8)
>>> b
{2, 4, 6}

 

 

3) 집합 연산 기능

a = { 1, 2, 3, 4 }
b = { 2, 4, 6, 8 }

# in, not in: 원소의 존재 유무 반환
>>> 1 in a
True
>>> 1 not in b
True

# &: 교집합 반환
>>> a & b
{2, 4}

# |: 합집합 반환
>>> a | b
{1, 2, 3, 4, 6, 8}

# -: 차집합 반환
>>> a - b
{1, 3}

 

여기서 교집합, 합집합, 차집합은 다음의 함수를 이용해서도 구할 수 있다.

# intersection(): 교집합 반환
>>> a.intersection(b)
{2, 4}

# union(): 합집합 반환
>>> a.union(b)
{1, 2, 3, 4, 6, 8}

# difference(): 차집합 반환
>>> a.difference(b)
{1, 3}

 


 

4. 딕셔너리

딕셔너리는 JSON 과 비슷하게 생각하면 좋을 것 같다.

 

리스트나 튜플과 마찬가지로 여러 데이터를 함께 이용할 때 사용하지만,

차이점이라면 딕셔너리는 각 데이터를 인덱스가 아닌 key값으로 접근한다.

따라서 key값은 딕셔너리 내에서 고유한 값이어야 한다.

 

딕셔너리는 중괄호{}를 사용한다.

 

1) 선언 및 초기화

# 빈 딕셔너리 선언
d = {}

# 딕셔너리 초기화
d = { 'key' : 'value' }

 

만약 딕셔너리 초기화 시 동일한 key값을 입력하면 오류가 발생할까?

d = { 'key' : 'value1', 'key' : 'value2' }
print(d) # { 'key' : 'value2' }

오류는 발생하지 않고, 동일한 key값이 입력되면 value는 가장 마지막에 입력한 값으로 초기화되는 것을 알 수 있다.

 

 

2) 원소 접근 및 수정

인덱스 대신 key를 사용하므로 원소에 접근할 때에도 key값을 사용하여 접근하면 된다.

d = { 'a' : 'A', 'b' : 'B', 'c' : 'C' }

print(d['a']) # 'A'

d['a'] = 'a' # d = { 'a' : 'a', 'b' : 'B', 'c' : 'C' }

 

 

3) 원소 추가

딕셔너리에 새로운 원소를 추가하고 싶은 경우

추가하고자 하는 key와 value를 이용해, 원소에 접근할 때와 마찬가지 문법으로 추가할 수 있다.

d['d'] = 'D'

# d = { 'a' : 'a', 'b' : 'B', 'c' : 'C', 'd' : 'D' }

 

 

4) 원소 삭제

딕셔너리의 원소를 삭제하고 싶은 경우

삭제하고자 하는 key값과 del 키워드를 이용해 삭제할 수 있다.

del dic['a']

# d = { 'b' : 'B', 'c' : 'C', 'd' : 'D' }

 

 

5) 딕셔너리 순회

딕셔너리는 key를 인덱스로 사용하기 때문에 순회를 하려고 할 때 난감하게 느껴질 수 있다.

그러나 다음의 함수를 사용하면 쉽게 해결이 된다.

 

- 1 : 순회할 때 key와 value 를 사용하려고 하는 경우, items()를 이용하면 된다.

d = { 'first' : 1, 'second' : 2, 'third' : 3 }

for key, value in d.items():
	print(key, value)

'''
print:

first 1
second 2
third 3
'''

 

- 2 : 순회할 때 index와 key를 사용하려고 하는 경우, enumerate()를 이용하면 된다.

d = { 'first' : 1, 'second' : 2, 'third' : 3 }

for index, key in enumerate(d):
	print(index, key)

'''
print:

0 first
1 second
2 third
'''

 

 

'Programming > Python' 카테고리의 다른 글

Ubuntu에서 uWSGI - Nginx 로 웹서버 배포하기  (0) 2021.09.11
[Python] 프로그램 종료  (0) 2021.06.12
[Python] 파일 입출력  (0) 2021.05.17

파일을 사용하는 전체적인 방식: 파일 열기 -> 파일 읽기/쓰기 -> 파일 닫기

 


 

1. 파일 열기

f = open(filename, mode [, encoding])

filename: 사용하고자 하는 파일 이름

mode: 사용하고자 하는 파일의 사용 목적(모드)

구분(mode) 텍스트 모드 이진 모드 방식
파일 입력 r rb 읽기(read)
파일 출력 w wb 쓰기(write)
a ab 추가(append)

* 텍스트 모드: 일반 텍스트 형태 그대로 사용하는 경우

* 이진 모드: 파일을 바이트 형태로 사용할 경우,

                   일반 텍스트 파일을 이진 모드로 열게되면 텍스트 글자를 바이트 단위로 사용할 수 있다.

 

* 인코딩 방식을 지정하는 경우(선택)

f = open("file.txt", "r", encoding="utf-8")

 

이렇게 만든 스트림을 f 객체에 넣어 이후 파일을 읽거나 쓸 때, 그리고 스트림을 다시 닫을 때 사용할 수 있도록 한다.

 

 

2. 파일 읽기/쓰기

- 파일 읽기 (mode: r)

1) 파일 내용 전부 읽기

str = f.read()

파일 내용이 전부 str 변수에 저장된다.

파일의 끝에 도달하면 빈 문자열('')을 반환한다.

 

2) 원하는 크기만 읽기

str = f.read(size)

size만큼의 내용만 str변수에 저장된다.

 

3) 파일 한 줄만 읽기

str = f.readline()

빈 문자열의 경우 대게 문자열의 끝에 붙게 되는 개행문자('\n')를 반환하고,

아예 파일의 끝에 도달한 경우 빈 문자열('')을 반환한다.

 

4) 파일의 모든 줄을 리스트로 읽기

s = list(f) # 또는
s = f.readlines()

 

파일의 모든 내용이 줄 단위로 리스트에 저장된다.

 

- 파일 쓰기 (mode: w)

f.write(value)

value 내용이 파일 안에 저장된다. (덮어쓰기 방식으로 저장.)

 

- 파일 쓰기 (mode: a)

f.write(value)

value 내용이 파일 안에 저장된다. (파일 안에 원래 다른 내용이 있었으면 그 내용 뒤에 덧붙여짐.)

 

- 파일 객체 위치 반환

f.tell()

파일 객체 위치를 반환한다.

f가 이진 모드로 열린 경우에는 바이트 수를 반환하고, 텍스트 모드로 열린 경우에는 글자수를 반환한다.

seek함수에의해 위치가 변경된 경우 변경된 위치를 반환한다.

 

- 파일 객체 위치 변경

f.seek(offset, whence) # whence: 0, 1, 2

기준점 whence에 offset을 더한 위치로 커서를 이동한다고 생각하면 된다.

그 위치로 이동시킨 후 원하는 읽기 또는 쓰기 작업을 실행하면 그 위치에서부터 읽거나 쓸 수 있다.

whence가 0이면 파일의 시작위치, 1이면 현재 위치, 2이면 파일의 끝이 기준점이 된다.

whence를 입력하지 않으면 기본으로 0에다가 offset을 더하는 것이 된다.

 

 

3. 파일 닫기

f.close()

동적할당에서 사용한 메모리를 꼭 free 시켜주는 것과 같이 파일 스트림 작업에서도 작업 후 꼭 close 시켜주도록 해야 한다.

 

 

 

+ Recent posts