코로나 때문에 개강도 연기되서 집에서만 살고 있다.
빨리 학교 가고 싶다...
'라쿠가키' 카테고리의 다른 글
방에 새 컴퓨터가 들어오다 (0) | 2020.08.30 |
---|---|
블로그를 만들다. (0) | 2020.03.11 |
코로나 때문에 개강도 연기되서 집에서만 살고 있다.
빨리 학교 가고 싶다...
방에 새 컴퓨터가 들어오다 (0) | 2020.08.30 |
---|---|
블로그를 만들다. (0) | 2020.03.11 |
dependencies:
flutter_staggered_grid_view: ^0.3.0
import 'package:flutter_staggered_grid_view/flutter_staggered_grid_view.dart';
new StaggeredGridView.countBuilder(
crossAxisCount: 4,
itemCount: 8,
itemBuilder: (BuildContext context, int index) => new Container(
color: Colors.green,
child: new Center(
child: new CircleAvatar(
backgroundColor: Colors.white,
child: new Text('$index'),
),
)),
staggeredTileBuilder: (int index) =>
new StaggeredTile.count(2, index.isEven ? 2 : 1),
mainAxisSpacing: 4.0,
crossAxisSpacing: 4.0,
)
crossAxisCount : 가로 타일의 길이이다. 위에서 확인했을 때 길이가 4임을 알 수 있다.
itemCount와 itemBuilder의 경우 일반적인 Builder와 사용법이 같다.
staggerdTileBuilder 에서 각 타일의 크기를 지정해줄 수 있는데,
위의 예시에서는 짝수인 경우 가로 세로 (2,2),
홀수인 경우 (2,1)의 크기를 갖게 하였다.
Shared Preferences (1) | 2020.04.28 |
---|---|
Cupertino ActionSheet (0) | 2020.04.05 |
image shake animation (0) | 2020.04.04 |
Flutter Search-bar 구현 (0) | 2020.03.13 |
플러터 - 크로스 플랫폼 (1) | 2020.03.11 |
게시물을 검색하기 위한 Search-bar를 구현해보았다.
검색하고 싶은 값은 TextField 위젯을 사용했는데, 보통 컨트롤러로 입력 받은 값을 사용하지만, 나는 실시간으로 입력되는 글을 바로 바로
서버로 보내는 것을 하고 싶어서 onChanged() 를 사용했다.
TextField(
focusNode: _focus,
keyboardType: TextInputType.text,
onChanged: (text){
_streamSearch.add(text);
},
decoration: InputDecoration(
hintText: lang(0),
border: InputBorder.none,
icon: Padding(
padding: EdgeInsets.only(left: 13),
child: Icon(Icons.search))),
),),
입력된 값은 _streamSeach 라는 미리 만들어둔 스트림 통로를 타고 String값이 이동하는데, 이 통로와 연결되어 있는
StreamBuilder가 통로에서 값을 받고 Node.js 서버에 전달한다.
StreamBuilder(
stream: _streamSearch.stream,
builder: (context,snapshot){
if(snapshot.hasData)
return FutureBuilder(
future: _fetchSearch(snapshot.data),
builder: (context,snaps){});
}
)
Node.js 에서는 아래와 같이 값을 전달받아 데이터베이스에 쿼리를 날려 검색을 하도록 하였다.
//--------------------------- 검색
app.get('/search/:name',(req,res)=> {
let sql = 'SELECT * FROM POSTS WHERE title REGEXP ?';
let params =[req.params.name];
connection.query(
sql,params,
(err, rows, fields) => {
res.send(rows);
}
);
});
만약 검색으로 '일'을 검색하였다면 돌아온 응답 값은 아래와 같다.
[{"id":15,"title":"일본이 전자 산업 분야에서 내수시장을 방어하는 방법","content":"일반 재단법인 일본 원자력 문화 재단에서 인용\n일본 전기의 주파수는 시즈오카 현의 후지 천 당을 경계로 동쪽은 50Hz, 서일본이 60Hz와 다릅니다.","date":"2020-03-10T15:00:00.000Z","views":99,"good":30,"bad":60,"pic_thumb":"/image/1f71317100b29d5250f418db15a7c700","tag_0":"일본","tag_1":"전압","tag_2":"시즈오카","tag_3":null,"tag_4":null},
{"id":23,"title":"일류를 꿈꾸는 삼류 조폭의 이야기","content":"송강호란 배우의 이름을 확실히 사람들에게 각인시켜준 작품이었음. 이후로 송강호는 한국을 대표하는 배우로 성장했지","date":"2020-03-11T02:24:54.000Z","views":54,"good":30,"bad":21,"pic_thumb":"/image/36127119c67709878706cdae88ef15e4","tag_0":"영화","tag_1":"송강호","tag_2":"기생충","tag_3":"한국인","tag_4":null},
{"id":25,"title":"핵무기가 만들어지면 벌어지는 일들","content":"첫번째 이유는 우방국의 핵우산, 두번째는 박정희 정부때 찌라씨로 만들고 있었다가 완성 직전에 들켜서 npt에 가입한 걸로 알고 있음","date":"2020-03-10T15:00:00.000Z","views":75,"good":30,"bad":15,"pic_thumb":"/image/fcd451dba2f33e30da8133428448a36e","tag_0":"한국","tag_1":"핵무기","tag_2":"박정희","tag_3":"대한민국","tag_4":null},
{"id":36,"title":"80년대 일본을 느껴보자 감성 시티팝 플레이","content":"버블 경제 당시 최고의 음향장비와 굉장한 녹음시설 그리고 최고의 인력들이 다시는 만들지 못할 음악을 만들어냈다","date":"2020-03-11T05:43:39.000Z","views":0,"good":0,"bad":0,"pic_thumb":null,"tag_0":null,"tag_1":null,"tag_2":null,"tag_3":null,"tag_4":null}]
포스트의 '일'이라는 문자가 포함되어 있기 때문이다.
위에서 받아온 값은 JSON형식이기 때문에, 클라이언트단에서 받아 각 필드들을 읽고 맵핑하여 구조체 안에 넣는 작업을 했다.
class Post {
final int postID;
final String title;
final String content;
final String date;
final String thumb;
final int views;
final int good;
final int bad;
Post(
{this.postID,
this.title,
this.content,
this.date,
this.thumb,
this.views,
this.good,
this.bad});
factory Post.fromJSON(Map<String, dynamic> json) {
return Post(
postID: json['id'],
title: json['title'],
content: json['content'],
date: json['date'],
thumb: json['pic_thumb'],
views: json['views'],
good: json['good'],
bad: json['bad'],
);
}
}
비동기식으로 이루어지는 것임으로 Future 로 캡슐화를 시켜두었다.
서버로부터 받은 값을 response 에 담아두고 위에서 설명한 맵핑 과정을 거친다.
Future<List<Post>> _fetchSearch(String name) async {
List<Post> _posts = [];
final response = await http.get('http://127.0.0.1/search/'+name);
final List<Post> parsedResponse = jsonDecode(response.body)
.map<Post>((json) => Post.fromJSON(json))
.toList();
_posts.clear();
_posts.addAll(parsedResponse);
return _posts;
}
위의 Future 식은 FutureBuilder와 연결하여 데이터를 다 받아왔을 경우 열려서 위젯을 return 하도록 설계하였다.
네트워크 기반의 어플은 서버로부터 데이터를 주고 받는 작업이 필수이다.
플러터를 하면서 http와의 통신에 대해 공부를 많이 하고 있는데 그중에 스트림(Stream)과
비동기(Async) 프로그래밍의 매력을 크게 느끼고 있다.
이것을 잘 정리해 두어 후에 다른 앱을 개발할 때 두고 두고 응용해야겠다
_끗
Shared Preferences (1) | 2020.04.28 |
---|---|
Cupertino ActionSheet (0) | 2020.04.05 |
image shake animation (0) | 2020.04.04 |
flutter_staggered_grid_view (0) | 2020.03.15 |
플러터 - 크로스 플랫폼 (1) | 2020.03.11 |
Computer Networking : A Top Down Approach 7th Edition, Global Edition Jim Kurose, Keith Ross
목적지 호스트의 Transport-layer 은 바로 아래의 Network-layer 로부터 Segment를 수신 받는다.
Segment를 받은 Transport-layer은 호스트에서 실행중인 애플리케이션 프로세스에게 이 Segment의
데이터를 전달한다. 이 때, 프로세스는 소켓(socket)에서 이 데이터를 받아들인다.
위 그림에서 볼 수 있듯이 소켓(socket)은 Application-layer 와 Transport-layer의 사이에 위치하여,
데이터를 서로에게 전달해주는 중재자 역할을 한다. 그런데 이 소켓은 꼭 1개만 존재하는 것이 아니라
여러 개가 존재할 수가 있다. 이때, 소켓을 구별하기 위한 식별자를 가지게 된다.
소켓의 갯수가 1개가 아니기 때문에, Transport-layer에서 받은 Segment를 구별하여 각각 지정된 소켓에
보내줄 필요가 있다.
따라서 이를 위해, Segment에 소켓을 구별하기 위한 필드를 추가를 하게 된다.
포트번호는 0~65535까지의 16비트 정수이며, 1~1023번까지의 포트는 잘 알려진 포트 번호(Well-Known Port
Number)라고 하여 사용을 제한한다.
<주요 포트 번호>
소켓은 다른 소켓과 구분되는 자신의 포트번호를 할당 받는다. 따라서 Transport-layer에서 Segment 안의 목적지 포트 번호를 확인하고, 이와 맞는 포트 번호를 가진 소켓으로 해당 Segment를 전달한다. Segment를 받은 소켓과 연결된
Application은 해당 데이터를 무사히 받을 수 있게 된다.
여기서 Transport-layer의 Segment를 알맞은 소켓으로 보내는 작업을 Demultiplexing이라고 한다.
최초로 데이터가 출발한 호스트에서 Segment 앞에 Header를 붙혀 캡슐화하고
Network-layer로 내려보내는 작업을 Mutiplexing이라고 한다.
UDP 소켓 9157을 가진 호스트 A의 프로세스 P3에서 UDP 소켓 6428을 가진 호스트 B의 프로세스
P1에게 데이터 전송을 한다고 가정하자.
Multiplexing
이때, 호스트 A에서의 Transport-layer에서는 프로세스 P3 -> 소켓으로부터 전달 받은 데이터와 출발지 포트 번호(9157),
도착지 포트 번호(6428) + α 를 가지고 있는 Segment를 생성한다. 이 Segment를 Network-layer로 전달한다.
Segment를 받은 Network-layer은 이 것을 IP Datagram으로 캡슐화하고 best-effort delivery Service 로 목적지인
호스트B로 전달한다.
Demultiplexing
호스트 B의 Transport-layer에서는 받은 Segment 안의 목적지 포트 번호(6428)를 확인하고, 일치하는 포트 번호를
갖는 소켓에 해당 Segment를 전달한다.
※ 그럼 출발지 포트 번호는 언제 사용하는가?
출발지 포트 번호는 데이터를 준 호스트에게 응답을 보낼 때 사용한다.
편지를 보낼 때 본인의 주소를 적어야지 받은 사람으로부터 답장을 받을 수 있는 것과 비슷한 이치이다.
TCP 소캣은 4개의 요소(tuple)로 식별된다.
따라서, TCP Segment를 전달 받았다면 알맞은 socket에 전달하는 작업(Demultiplexing)을 위해 4개의 값을 사용한다.
호스트 A의 프로레스 P3가 호스트 B의 프로세스 P4에게 데이터를 보낼 때
P3 : src IP : address A | src Port : 9157 | dest IP : address B | dest Port : 80
호스트 C의 프로세스 P2, P3가 호스트 B의 프로세스 P3에게 데이터를 보낼 때
P2 : src IP : address C | src Port : 5775 | dest IP : address B | dest Port : 80
P3 : src IP : address C | src Port : 9157 | dest IP : address B | dest Port : 80
프로세스 P2, P3는 출발지 포트 번호가 각각 5775, 9157로 다르기 때문에 다른 소켓으로 향한다.
그렇다면 연결이 많이 생긴다면 그만큼 소켓의 개수가 늘어날까?
Server는 Client에서 오는 Segment를 구별하여 많으면 많을수록 연결 소켓을 많이 생성하며,
각 연결을 따라서 새로운 프로세스도 많이 만들 것이다.
하지만, 오늘날의 고성능 Sever들은 하나의 프로세스만을 사용한다.
새로운 Client와의 연결을 위해 새로운 연결 소켓과 함께 Thread를 만들어 사용한다.
아직까지 특별하게 어려운 것은 없는 거 같다.
중학생 시절, Visual basic으로 Winsock을 이용해 멀티 chat 같은 걸 만들어보곤 했는데
그때 찾아봤던 내용인 것 같다. 전공으로 들으니 재밌구만.
_끗
Computer Network[04] - principle of reliable data transfer(1) (0) | 2020.03.23 |
---|---|
Computer Network[03] - connectionless transport : UDP (0) | 2020.03.22 |
MySQL 기본 쿼리 명령어 (0) | 2020.03.19 |
[Database] Database Management System & SQL (0) | 2020.03.12 |
Computer Network[01] - Transport Layer (0) | 2020.03.12 |
npm install react-router-dom
link tag를 사용하는 전통적인 웹 방식은 새로운 페이지 요청 시마다 정적 리소스가 다운되고
전체 페이지를 다시 렌더링하는 방식을 사용한다.
즉, 새로고침이 되어 변경이 필요없는 부분까지 갱신되므로 비효율적이다.
SPA는 웹 애플리케이션에 필요한 모든 정적 리소스를 최초에 한번 다운로드한다.
이후 새로운 페이지가 요청 시 갱신에 필요한 데이터만 전달 받아 페이지를 갱신하므로
전체적인 트래픽을 감소할 수 있다.
또한 전체 렌더링이 아니라 변경이 필요한 부분만 갱신한다.
리액트는 SPA(Single Page Application)를 만드는 데 최적화되어 있다.
별도로 다른 페이지를 이동하는 Routing 기능은 제공하지 않는다.
React는 React-Router라는 라이브러리를 사용하면 Rounting을 할 수 있다.