728x90
728x90

background-image에서 조정할 수도 있지만 바로 img나 video태그에서 cover 효과처럼 적용하고 싶을 때 크롬 등에서는 css 에 object-fit를 적용하면 되는데, ie는 지원하지 않기 때문에 css를 직접 작성해 주게 된다.

 

video {     
    position: absolute; 
    top: 50%; 
    left: 50%; 
    min-width: 100%; 
    min-height: 100%; 
    width: auto; 
    height: auto; 
    z-index: -100; 
}
transform: translateX(-50%) translateY(-50%); 

 

 

참고

background-size 속성 (배경이미지 크기 조절) 정리

728x90
728x90
블로그 이미지

coding-restaurant

코딩 맛집에 방문해주셔서 감사합니다.

,

[공유] C++

C, C++ 2021. 5. 12. 18:02
728x90
728x90
728x90
728x90

'C, C++' 카테고리의 다른 글

메인 함수(엔트리포인트)  (0) 2021.09.08
strcmp, strncmp - 두 문자열 일치 비교, 길이 특정  (0) 2021.09.08
[c] 파일포인터와 fopen, fclose, fprintf, fgets  (0) 2021.08.30
[C] strcpy, strncpy  (0) 2021.08.27
c++ define 함수  (0) 2021.05.26
블로그 이미지

coding-restaurant

코딩 맛집에 방문해주셔서 감사합니다.

,
728x90
728x90

viewport


IOS에서 페이지를 표시할 때 사용했던 단위. 이제 두루두루 쓰임. 간혹 익스플로러 중에 안되는 경우 대체 코드 : 

<head>
<style>
@-ms-viewport {width: device-width;}
@o-viewport {width: device-width;}
@viewport {width: device-width;}
</style>
</head>

 

 

* 너비를 장치너비로 설정

디바이스의 해상도가 아닌 실제 크기를 기준으로 너비, 높이를 잡기

<meta name="viewport" content="width=device-width">

 

* 높이를 장치높이로 설정

<meta name="viewport" content="height=device-height">

 

* 초기 화면 배율 설정 (zoom 레벨 설정)

<meta name="viewport" content="width=device-width, initial-scale=1">

 

* 최소/최대 화면 스케일 설정

각각 극단적 화면 축소, 확대를 방지한다.

<meta name="viewport" content="width=device-width, initial-scale=1, minimum-scale=1.0">

 

* 사용자의 크기 조절 막기

<meta name="viewport" content="user-scalable=no, width=device-width">

 

 

 

 

디바이스별 해상도 반응형 분기점


* 4가지, 3가지 분류로 나눴을 때

 

/* PC , 테블릿 가로 (해상도 768px ~ 1023px)*/ 
@media all and (min-width:768px) and (max-width:1023px) { /*스타일입력*/} 

/* 테블릿 세로 (해상도 768px ~ 1023px)*/ 
@media all and (min-width:768px) and (max-width:1023px) { /*스타일입력*/} 

/* 모바일 가로, 테블릿 세로 (해상도 480px ~ 767px)*/ 
@media all and (min-width:480px) and (max-width:767px) { /*스타일입력*/}

/* 모바일 가로, 테블릿 세로 (해상도 ~ 479px)*/ 
@media all and (max-width:479px) { /*스타일입력*/}
/* PC (해상도 1024px)*/ 
@media all and (min-width:1024px) { /*스타일입력*/} 

/* 테블릿 가로, 테블릿 세로 (해상도 768px ~ 1023px)*/ 
@media all and (min-width:768px) and (max-width:1023px) { /*스타일입력*/} 

/* 모바일 가로, 모바일 세로 (해상도 480px ~ 767px)*/ 
@media all and (max-width:767px) { /*스타일입력*/}

 

* 미디어쿼리 적용법

@media only|not screen and (min-width: 152px) and (max-width: 1024px) { ... }

 

only : 뒤의 조건만 / not : 뒤의 조건을 제외한 조건
미디어쿼리를 지원하는 브라우저 대상으로 쿼리 안의 내용이 적용됩니다.

-- 미디어 타입의 종류

  • all : 모든 미디어타입
  • aural : 음성 합성장치
  • braille : 점자 표시 장치
  • handheld : 

 

 

* 미디어쿼리 적용법

<link href="xxx.css" media="screen and (min-width: 152px) and (max-width: 1024px)" rel="stylesheet">
<style type="text/css" media="screen and (min-width: 152px) and (max-width: 1024px)"</style>
<style>
	@import url(xxx.css) screen and (min-width: 152px) and (max-width: 1024px);
</style>
/* css 파일 안이나 style 태그 안에서 */
@media screen and (min-width: 152px) and (max-width: 1024px)

 

 

 

출처 및 참조하면 좋을 곳들

aboooks.tistory.com/352

benfrain.com/understanding-the-viewport-meta-tag-and-css-viewport/

www.quirksmode.org/mobile/metaviewport/

apple 문서

eunyoe.tistory.com/entry/CSS-%EB%B0%98%EC%9D%91%ED%98%95-%EC%9B%B9-%EB%94%94%EB%B0%94%EC%9D%B4%EC%8A%A4%EB%B3%84-%ED%95%B4%EC%83%81%EB%8F%84-%EB%B0%98%EC%9D%91%ED%98%95-%EB%B6%84%EA%B8%B0%EC%A0%90-%EB%A6%AC%EC%8A%A4%ED%8A%B8

www.nextree.co.kr/p8622/

728x90
728x90

'Javascript' 카테고리의 다른 글

마우스로 드래그한 텍스트 가져오기  (0) 2021.06.14
window.open 후 자식창에서 자식창의 함수 실행  (0) 2021.06.03
  (0) 2021.03.17
스택으로 뒤로가기/앞으로가기  (0) 2021.03.16
[JS] substr()  (0) 2021.03.11
블로그 이미지

coding-restaurant

코딩 맛집에 방문해주셔서 감사합니다.

,
728x90
728x90

의도 : 사용자가 웹페이지에서 조회한 페이지 내역, 변경한 옵션들의 값을 저장해 사용하려고 한다
(저장할 옵션들의 값이란 웹사이트의 폰트 크기, 팝업띄우기 기능 등 사용자가 기본값에서 설정한 것들)

의문 : 이 많은 정보들을 쿠키 한개로 저장해 관리하는게 나을까, 여러줄로 저장해서 사용하는 게 나을까 혹은 권장될까

조언받은 내용 : 어떤 형식으로 쿠키를 저장하면 좋을 지 판단할 때,
브라우저별로 달라야 하는 정보인지, 브라우저의 여부를 떠나 같은 사용자에게 어느정도 동일한 내용을 보여줄 것인지로 판단

사용자가 같을 때 일관되어야 하는 정보는 서버에 저장하는 편이고, 아닌건 쿠키나 로컬스토리지 등에 저장한다..(나도 사용자별 쿠키는 서버에 저장하는 게 낫지 않을까 생각하는데 내가 작업하는 영역에서 어떻게 저장해야할지는 이부분에 대해서 팀원에 물어봐야겠다..)

암튼 요약하자면...쿠키 형식에 정답은 없다..

728x90
728x90
블로그 이미지

coding-restaurant

코딩 맛집에 방문해주셔서 감사합니다.

,

Javascript 2021. 3. 17. 17:31
728x90
728x90


끝부분으로 데이터가 삽입, 앞부분으로는 데이터가 삭제되는 자료구조.
선입선출(FIFO)

 

용도


운영체제의 프로세스 처리순서, 프린트 목록, 대기줄 선착순

 

 

추상적인 데이터형


enqueue : 큐의 끝부분에 요소를 추가하는 함수

dequeue : 큐의 앞부분에서 요소를 삭제하는 함수

clear : 큐에서 모든 요소를 삭제하는 함수

front : 큐의 앞부분에 저장된 요소를 반환 (내용만 확인)

back : 큐의 끝부분에 저장된 요소를 반환 (내용만 확인)

empty : 큐의 요소가 있는지 여부를 반환하는 함수

toString : 큐의 모든 요소를 문자열로 변환해 반환하는 함수

 

* JS 배열에서 사용할 수 있는 함수

push() : 배열의 끝부분에 데이터를 추가하는 함수

shift() : 배열의 앞부분에 데이터를 삭제하는 함수

 

 

JS 큐 구현


1

//큐 생성자 함수
function Queue() {
    this.dataStore = []; 
    this.enqueue = enqueue;
    this.dequeue = dequeue;
    this.front = front;
    this.back = back;
    this.toString = toString;
    this.empty = empty;
}

//enqueue
//큐의 끝부분에 요소를 추가
function enqueue(element) {
    this.dataStore.push(element);
}

//dequeue
//큐의 앞부분에서 요소를 삭제
function dequeue() {
    return this.dataStore.shift();
}

//front
//큐의 앞부분에 저장된 요소 확인
function front() {
    return this.dataStore[0];
}

//back
//큐의 끝부분에 저장된 요소 확인
function back() {
    return this.dataStore[this.dataStore.length-1];
}

//toString
//큐의 모든 요소를 출력
function toString() {
    var retStr = "";
    for (var i = 0;i < this.dataStore.length; ++i )    {
        retStr += this.dataStore[i] + "\n";
    }
    return retStr;
}

//empty
//큐가 비어있는지 여부 확인
function empty() {
    if (this.dataStore.length == 0) {
        return true;
    } else {
        return false;
    }
}


//테스트
var q = new Queue();
q.enqueue("첫번째");
q.enqueue("두번째");
q.enqueue("셋번째");
print(q.toString());
q.dequeue();
print(q.toString());
print("Front of queue: " + q.front());
print("Back of queue: " + q.back());


function print(v) {
    document.write(v+"<br/>");
    //console.log(v);
} 

seagull-dev.tistory.com/14

 

2

var Queue = (function() {
  function Queue() {
    this.count = 0;
    this.head = null; // 맨 처음
    this.rear = null; // 맨 끝
  }
  
  function Node(data) {
    this.data = data;
    this.next = null;
  }
  
  Queue.prototype.enqueue = function(data) {
    var node = new Node(data);
    if (!this.head) {
      this.head = node;
    } else {
      this.rear.next = node;
    }
    this.rear = node;
    return ++this.count;
  };
  
  Queue.prototype.dequeue = function() {
    if (!this.head) { // stack underflow 방지
      return false;
    }
    var data = this.head.data;
    this.head = this.head.next;
    // this.head 메모리 클린
    --this.count;
    return data;
  };
  
  Queue.prototype.front = function() {
    return this.head && this.head.data;
  };
  return Queue;
})();

var queue = new Queue();
queue.enqueue(1); // 1
queue.enqueue(3); // 2
queue.enqueue(5); // 3
queue.dequeue(); // 1
queue.front(); // 3

// 출처 - 제로초
// https://www.zerocho.com/category/Algorithm/post/580b9b94108f510015524097

 

예제는 일반적인 큐지만, 특수한 형태의 큐 두가지가 있다.

 

1. 순환 큐 

front와 rear이 연결되어 있다. 아래를 추가해주면 된다. 메모리 관리가 쉬워서 사용하나 JS는 메모리 관리가 자동이라 효용성이 떨어진다.

this.rear.next = this.front

 

 

2. 우선순위 큐

enqueue 시 제일 뒤가 아니라 우선순위를 따져서 데이터를 넣는다. 단점은 데이터 삽입이 어렵다는 것인데 단점은 힙이라는 자료구조를 사용해서 보완한다. 힙을 이해하려면 자료구조 트리와 함께 공부하면 좋다. 

 

트리 :  나무의 뿌리같이 생긴 자료구조. 제일 첫번째 노드는 root이며, 제일 마지막 노드는 leaf라고 부른다. 그 둘을 제외한 노드들은 내부 노드라고 퉁쳐 부른다. 실생활에서 대진표 등에 사용된다.

힙 :  트리의 일종. 최대힙은 완전 트리이면서 Root가 모든 자식들보다 커야하며, 최소힙은 Root가 자식보다 작은 것. 

완전 트리 : 노드가 순서대로 들어있는 트리

 

 

소스 출처

www.zerocho.com/category/Algorithm/post/580b9b94108f510015524097

 

728x90
728x90
블로그 이미지

coding-restaurant

코딩 맛집에 방문해주셔서 감사합니다.

,
728x90
728x90

스택


밑이 막힌 상자같은 자료구조

 

용도


뒤로가기 등

 

스택의 구현 방법


배열, 연결리스트

 

스택의 추상적인 데이터형


top : 현재위치 프로퍼티

length : 스택에 포함된 요소 수 반환 함수

clear : 스택 모든 요소 삭제 함수

push : 스택에 새 요소 추가 함수

pop : 스택의 탑에 있는 요소 제거 후 반환 함수

peek : 스택의 탑에 있는 요소 내용 반환 함수

empty : 스택에 요소가 있는지 여부 반환 함수

 

 

js 스택 구현


1

//스택 생성자 함수
function Stack() {
    this.dataStore = []; //스택요소를 저장하는 배열
    this.top = 0;
    this.push = push;
    this.pop = pop;
    this.peek = peek;
    this.clear = clear;
    this.length = length;
}

//push
function push(element) {
    this.dataStore[this.top++] = element; //현재 top위치에 새요소를 추가한 다음 top이 증가
}

//pop
function pop() {
    return this.dataStore[--this.top]; //스택의 탑 위치에 있는 요소를 반환한 다음 top변수를 감소
}

//peek : 스택의 탑에 있는 요소 내용 반환 
function peek() {
    return this.dataStore[this.top-1];
}

//length
function length() {
    return this.top;
}

//clear
function clear() {
    this.top = 0; //top변수를 0으로 설정하면 스택 전체 요소가 삭제
}

//스택 클래스 구현 테스트
var s = new Stack();
s.push("main실행");
s.push("println실행");
s.push("timer실행");
print("length: " + s.length());
print(s.peek());

var popped = s.pop();
print("The popped element is: " + popped);
print(s.peek());
s.push("프로그램 종료");
print(s.peek());
s.clear();
print("length: " + s.length()); 
print(s.peek()); //undefined , 스택에 모든 요소를 삭제했으므로 탑위치에 데이터가 없는 상황



function print(v) {
    document.write(v+"<br/>");
    //console.log(v);
}

//[출처] [자료구조] 자바스크립트로 스택(Stack) 구현 |작성자 자바킹
//http://blog.naver.com/PostView.nhn?blogId=javaking75&logNo=220226369586&categoryNo=71&parentCategoryNo=0&viewDate=&currentPage=1&postListTopCurrentPage=1&from=postView

 

2

class Stack {
  constructor() {
    this.arr = [];
    this.index = 0;
  }
  push(item) {
    this.arr[this.index++] = item;
  }
  pop() {
    if (this.index <= 0) return null;
    const result = this.arr[--this.index];
    return result;
  }
}

let stack = new Stack();
stack.push(1); // arr: [1], index: 1
stack.push(2); // arr: [1, 2], index: 2
stack.push(3); // arr: [1, 2, 3], index: 3
console.log(stack.pop()); // 3 , index: 2
console.log(stack.pop()); // 2 , index: 1
console.log(stack.pop()); // 1 , index: 0
console.log(stack.pop()); // null

//https://velog.io/@kimhyo_0218/JavaScript-%EC%8A%A4%ED%83%9DStack-%EA%B5%AC%ED%98%84%ED%95%98%EA%B8%B0

 

3

   constructor(size){
        this.size = size;
        this.top = 0;
        this.array = [];
    }
   pop(){
        let temp = this.array[this.top];
        this.top--;
        return temp;
    }

    push(item){
        if(this.size > this.top){
            this.top++;
            return this.array[this.top] = item;
        }else{
            console.log(new Error("stack is full"));
        }
    }
    
    peek(){
        return this.array[this.top];
    }
function Stack(max_size){
    
    const size = max_size;
    let top = 0;
    let array = [];
    
    return{
        pop(){
             if(top==0){
                console.log("stack is empty");
            }else{
                let temp = array[top];
                top--;
                return temp;
            }
        },
    
        push(item){
            if(size > top){
                top++;
                return array[top] = item;
            }else{
                console.log(new Error("stack is full"));
            }
        },
        peek(){
            return array[top];
        }
    }
  
}

var a = Stack(5);
a.push(1);
a.push(2);
a.push(3);
a.push(4);
a.push(5);
console.log(a.pop()); // 5
console.log(a.peek()); // 4
//https://hokeydokey.tistory.com/30

 

 

 

자바에서 배열을 이용한 스택 구현


구현 : 1

 

 

url이 바뀌지 않는 싱글페이지에서 뒤로가기, 앞으로가기 구현


 

스택 자료구조로 브라우저에서 뒤로가기, 앞으로가기 코드를 구현중... 뒤로가기용, 앞으로가기용 두 개의 스택이 필요하다고 생각.

 

1. 메뉴바에 보여지는 것

          <------ 시간 (최근) ------

메뉴바     4      3      2      1

 

2. 시뮬레이션

 

          <------ 시간 (최근) ------

히스토리 메뉴바     

아무것도 없는 상태

 

[히스토리 기능 테스트 시작]

메뉴 1 클릭

히스토리 메뉴바     1

메뉴바 표시를 위한 히스토리 목록에 1 저장
뒤로가기용 스택에 히스토리 갱신 전 마지막 요소는 없기 때문에 저장되지 않음

 

메뉴 2 클릭

히스토리 메뉴바     2      1

뒤로가기용 스택에 히스토리 갱신 전 마지막 요소(1)를 저장, 뒤로가기용 스택에는 1이 들어있음
히스토리 메뉴바 반환 시 좌측을 최신 것으로 정렬

 

메뉴 3 클릭

히스토리 메뉴바     3      2      1

뒤로가기용 스택 히스토리 갱신 전 마지막 요소(2) 저장, 1, 2가 들어있음

 

메뉴 4 클릭

히스토리 메뉴바     4      3      2      1

뒤로가기용 스택 히스토리 갱신 전 마지막 요소(3) 저장, 1, 2, 3이 들어있음

 

[뒤로가기 테스트 시작]

뒤로가기 한 번 시행

히스토리 메뉴바     3      4      2      1

히스토리 갱신 전 마지막 요소를 앞으로가기용 스택에 저장(4). 과거 보고 있는 곳도의미.

앞으로가기용 스택에 저장된 것 : 4

뒤로가기용 스택의 마지막 요소(3)를 삭제, 1, 2가 들어있음 

앞으로가기 버튼 활성화(이전에는 비활성화) 

 

뒤로가기 한 번 시행

히스토리 메뉴바     2      3      4      1

갱신 전 마지막 요소를 앞으로가기용 스택에 저장(3). 과거 보고 있는 곳도의미. 

앞으로가기용 스택에 저장된 것 : 4, 3

뒤로가기용 스택 마지막 요소(3)를 삭제, 1, 2 들어있음 

 

[앞으로가기 테스트 시작]

앞으로가기 한 번 시행

히스토리 메뉴바     3      2      4      1

앞으로가기용 스택에 마지막 요소(3) 삭제. 현재 보고있는 곳도 의미, 저장된 것 : 4

뒤로가기용 스택에 갱신 전 마지막 요소 추가(2), 저장된 것 : 1, 2

 

앞으로가기 한 번 시행

히스토리 메뉴바     4      3      2      1

앞으로가기용 스택에 마지막 요소(4) 삭제. 현재 보고있는 곳도 의미, 저장된 것 :  없음

앞으로가기용 스택에 요소가 없으면 앞으로가기 버튼 비활성화

뒤로가기용 스택에 갱신 전 마지막 요소 추가(3), 저장된 것 : 1, 2, 3

 

[뒤로가기 테스트 시작]

뒤로가기 한 번 시행

히스토리 메뉴바     3      4      2      1

갱신 전 마지막 요소를 앞으로가기용 스택에 저장(4). 과거 보고 있는 곳도의미. 

앞으로가기용 스택에 저장된 것 :  4

뒤로가기용 스택 마지막 요소(3)를 삭제, 1, 2 들어있음 

앞으로가기 버튼 활성화

 

* 히스토리 목록은 스택과 별개로 본 지 오래된 동일한 요소를 지운 뒤 최근에 본 것만 뒤에 쌓는 식으로 동작.

 

 

 

history 객체에 대해서 > click!


원래 이 기능을 수행하는 history 객체의 기능을 사용할 수도 있다.
내가 history를 사용하지 못한 이유는 웹이 로그인 제외.. 싱글 페이지이며 원 페이지 내에서 각 목차별 콘텐츠로 페이지를 이동하는 것처럼 해야하다보니 그와 비슷하게 동작하는 것을 개발해야 했다. 브라우저의 앞/뒤로가기를 누르면 로그인하는 첫 페이지로 이동해버린다.

구현해야 하는 기능은 조회한 목차별 콘텐츠의 이력이 저장되며, 뒤로가기 앞으로가기가 동작해야 했다.  
그리고 로그인 시 조회했던 목록에 대한 정보를 다시 들고와야한다. 

막연하게 브라우저에서 썼던 히스토리 기능, 특히 앞으로가기가 뒤로가기 기록에 대해 의존하는 걸 다시 확실히 알았다. 

728x90
728x90
블로그 이미지

coding-restaurant

코딩 맛집에 방문해주셔서 감사합니다.

,
728x90
728x90

WebRTC


웹, 네이티브 앱에서 오디오, 비디오, 데이터의 실시간 통신을 가능하게 하는 오픈 소스 프로젝트. 즉 웹 브라우저에서 화상채팅, 음성채팅 등을 제작 가능한데, 스트리밍 데이터를 얻고 통신하기 위한 API가 구현되어 있다. (출처:구글코드랩)

몇 가지 JS API를 알아보면...

 

getUserMedia() : 오디오, 비디오를 캡쳐


MediaRecorder : 오디오, 비디오를 녹화


RTCPeerConnection : 사용자간 오디오, 비디오를 스트리밍

WebRTC는 RTCPeerConnection을 사용하여 브라우저간에 스트리밍 데이터를 전달하는데, 통신을 조정하고 신호로 알려진 프로세스 인 제어 메시지를 보내는 메커니즘이 필요하다. 이 시그널링 방법과 프로토콜은 WebRTC에서 지정하지 않고 보통 메시징에 Socket.IO를 사용한다.

RTCDataChannel : 사용자간 데이터를 스트리밍 

 

 

 

사용처


파폭, 오페라, 등 브라우저와 데스크톱, 안드로이드 크롬, ios, 안드로이드 기본 앱에서 사용 가능

 

 

실습 예제 목차


웹캠으로 비디오를 가져오고, 스냅샷을 찍고, P2P를 공유하는 앱을 빌드하는 예제이다. WebRTC API 사용 방법을 배우며 Node.js를 사용하여 메시징 서버를 설정할 수 있다. 웹캠에서 비디오 가져오기, RTCPeerConnection으로 비디오 스트리밍, RTCDataChannel를 사용한 데이터 스트리밍, 메시지 교환을 위한 신호 서비스 설정, 피어 연결 및 신호 결합, 스냅샷을 찍어 데이터 채널을 통해 공유하기 등을 해 본다.

요구사항은 Chrome 47 이상, Chrome용 웹서버나 다른 웹 서버, 샘플 코드, 텍스트 편집기, 아래 선행 지식이다.

웹 서버로 파이어베이스(구글 Firebase)를 사용했다.

 

선행 지식 : HTML, CSS, JS, NodeJS, SSL, Soket, IO

 

 

1~3. 네트워크 없이 실행

1. 카메라에서 비디오 추출

2. RTCPeerConnection을 이용해서 상대에게 비디오 제공 (네트워크 x)

3. RTCDataChannel을 이용해서 데이터 주고받기

 

4~6. 네트워크 이용, NodeJS 설치 후 웹으로 실행

4. 서버를 이용해서 메세지 주고받기 (signaling) 

5. 네트워크 상에서 비디오 주고받기 : getUserMedia, signaling, RTCPeerConnection의 종합 예제

6. 데이터 공유(제공)

 

 

예제 관련 주의사항

※ 구글 예제는 크롬에서만 작동하여 다른 브라우저에서 작동을 원할 시 추가 작업이 필요하다.

※ 네트워크 연결 시 SSL이 기본적으로 필요

※ 대화 상대를 찾기 위해 사용하는 서버가 한번만 연결되도록 만들어져 서버 재가동이 필요

※ 해당 예제는 오디오는 제공되지 않음, 위 사항들은 코드 일부 수정으로 보완 가능

 

 

 

구글 예제 코드 (다운)


 

CMD에서 step-05 디렉토리로 이동하여 npm i (설치) 를 명령하여 필요한 라이브러리를 설치한다.

 

C:\경로\step-05> dir
npm i

 

 

 

node index.js를 실행하여 웹서버를 실행
성공하면 아무런 메세지가 리턴되지 않는다

 

node index.js

 

크롬 브라우저 주소창에 http://localhost:8080 을 입력한다

카메라 사용 권한 요청화면 > 허용 클릭

 

 

서버 IP는 NodeJS를 실행한 컴퓨터 콘솔창에서 ipconfig나 ifconfig를 입력하면 확인 가능

 

  • index.js - NodeJS로 실행한 웹 서버 및 Signaling
  • index.html - 보여주는 첫 웹페이지. 화상채팅 화면.
    • js/main.js - RTCPeerConnection과 같은 실제 화상채팅을 하는 클라이언트 코드
    • css/main.css - index.html 에서 사용할 디자인

 

 

오류 정리


 

npm notice created a lockfile as package-lock.json. You should commit this file.

 

원인 : package.json에 해당 정보가 없어서

개인용 프로젝트거나 git이나 desc를 추가하지 않는다는 가정하에 description 쉼표 다음에 "private": true 추가 (출처)

 

 

getusermedia() error notfounderror

 

MediaDevices.getUserMedia()

반환값은 객체로 이행하는 Promise로 사용자가 권한 요청을 거부했거나 일치하는 유형의 미디어를 사용할 수 없는 경우 NonAllowedError와 NotFoundError 을 반환한다. 즉 지금 연결된 카메라가 없어서 뜬 메세지

 

 

 

 

주요 파일의 소스 구조


index.html

<!DOCTYPE html>
<html>

<head>

  <title>Realtime communication with WebRTC</title>

  <link rel="stylesheet" href="/css/main.css" />

</head>

<body>

  <h1>Realtime communication with WebRTC</h1>

  <div id="videos">
    <video id="localVideo" autoplay muted playsinline></video>
    <video id="remoteVideo" autoplay playsinline></video>
  </div>

  <!-- This file is automatically added/served when running "node index.js". -->
  <script src="/socket.io/socket.io.js"></script>
  <script src="https://webrtc.github.io/adapter/adapter-latest.js"></script>
  <script src="js/main.js"></script>

</body>

</html>

 

main.css

 

body {
  font-family: sans-serif;
}

video {
  max-width: 100%;
  width: 320px;
}

 

main.js

'use strict';

var isChannelReady = false;
var isInitiator = false;
var isStarted = false;
var localStream;
var pc;
var remoteStream;
var turnReady;

var pcConfig = {
  'iceServers': [{
    'urls': 'stun:stun.l.google.com:19302'
  }]
};

// Set up audio and video regardless of what devices are present.
var sdpConstraints = {
  offerToReceiveAudio: true,
  offerToReceiveVideo: true
};

/////////////////////////////////////////////

var room = 'foo';
// Could prompt for room name:
// room = prompt('Enter room name:');

var socket = io.connect();

if (room !== '') {
  socket.emit('create or join', room);
  console.log('Attempted to create or  join room', room);
}

socket.on('created', function(room) {
  console.log('Created room ' + room);
  isInitiator = true;
});

socket.on('full', function(room) {
  console.log('Room ' + room + ' is full');
});

socket.on('join', function (room){
  console.log('Another peer made a request to join room ' + room);
  console.log('This peer is the initiator of room ' + room + '!');
  isChannelReady = true;
});

socket.on('joined', function(room) {
  console.log('joined: ' + room);
  isChannelReady = true;
});

socket.on('log', function(array) {
  console.log.apply(console, array);
});

////////////////////////////////////////////////

function sendMessage(message) {
  console.log('Client sending message: ', message);
  socket.emit('message', message);
}

// This client receives a message
socket.on('message', function(message) {
  console.log('Client received message:', message);
  if (message === 'got user media') {
    maybeStart();
  } else if (message.type === 'offer') {
    if (!isInitiator && !isStarted) {
      maybeStart();
    }
    pc.setRemoteDescription(new RTCSessionDescription(message));
    doAnswer();
  } else if (message.type === 'answer' && isStarted) {
    pc.setRemoteDescription(new RTCSessionDescription(message));
  } else if (message.type === 'candidate' && isStarted) {
    var candidate = new RTCIceCandidate({
      sdpMLineIndex: message.label,
      candidate: message.candidate
    });
    pc.addIceCandidate(candidate);
  } else if (message === 'bye' && isStarted) {
    handleRemoteHangup();
  }
});

////////////////////////////////////////////////////

var localVideo = document.querySelector('#localVideo');
var remoteVideo = document.querySelector('#remoteVideo');

navigator.mediaDevices.getUserMedia({
  audio: false,
  video: true
})
.then(gotStream)
.catch(function(e) {
  alert('getUserMedia() error: ' + e.name);
});

function gotStream(stream) {
  console.log('Adding local stream.');
  localStream = stream;
  localVideo.srcObject = stream;
  sendMessage('got user media');
  if (isInitiator) {
    maybeStart();
  }
}

var constraints = {
  video: true
};

console.log('Getting user media with constraints', constraints);

if (location.hostname !== 'localhost') {
  requestTurn(
    'https://computeengineondemand.appspot.com/turn?username=41784574&key=4080218913'
  );
}

function maybeStart() {
  console.log('>>>>>>> maybeStart() ', isStarted, localStream, isChannelReady);
  if (!isStarted && typeof localStream !== 'undefined' && isChannelReady) {
    console.log('>>>>>> creating peer connection');
    createPeerConnection();
    pc.addStream(localStream);
    isStarted = true;
    console.log('isInitiator', isInitiator);
    if (isInitiator) {
      doCall();
    }
  }
}

window.onbeforeunload = function() {
  sendMessage('bye');
};

/////////////////////////////////////////////////////////

function createPeerConnection() {
  try {
    pc = new RTCPeerConnection(null);
    pc.onicecandidate = handleIceCandidate;
    pc.onaddstream = handleRemoteStreamAdded;
    pc.onremovestream = handleRemoteStreamRemoved;
    console.log('Created RTCPeerConnnection');
  } catch (e) {
    console.log('Failed to create PeerConnection, exception: ' + e.message);
    alert('Cannot create RTCPeerConnection object.');
    return;
  }
}

function handleIceCandidate(event) {
  console.log('icecandidate event: ', event);
  if (event.candidate) {
    sendMessage({
      type: 'candidate',
      label: event.candidate.sdpMLineIndex,
      id: event.candidate.sdpMid,
      candidate: event.candidate.candidate
    });
  } else {
    console.log('End of candidates.');
  }
}

function handleCreateOfferError(event) {
  console.log('createOffer() error: ', event);
}

function doCall() {
  console.log('Sending offer to peer');
  pc.createOffer(setLocalAndSendMessage, handleCreateOfferError);
}

function doAnswer() {
  console.log('Sending answer to peer.');
  pc.createAnswer().then(
    setLocalAndSendMessage,
    onCreateSessionDescriptionError
  );
}

function setLocalAndSendMessage(sessionDescription) {
  pc.setLocalDescription(sessionDescription);
  console.log('setLocalAndSendMessage sending message', sessionDescription);
  sendMessage(sessionDescription);
}

function onCreateSessionDescriptionError(error) {
  trace('Failed to create session description: ' + error.toString());
}

function requestTurn(turnURL) {
  var turnExists = false;
  for (var i in pcConfig.iceServers) {
    if (pcConfig.iceServers[i].urls.substr(0, 5) === 'turn:') {
      turnExists = true;
      turnReady = true;
      break;
    }
  }
  if (!turnExists) {
    console.log('Getting TURN server from ', turnURL);
    // No TURN server. Get one from computeengineondemand.appspot.com:
    var xhr = new XMLHttpRequest();
    xhr.onreadystatechange = function() {
      if (xhr.readyState === 4 && xhr.status === 200) {
        var turnServer = JSON.parse(xhr.responseText);
        console.log('Got TURN server: ', turnServer);
        pcConfig.iceServers.push({
          'urls': 'turn:' + turnServer.username + '@' + turnServer.turn,
          'credential': turnServer.password
        });
        turnReady = true;
      }
    };
    xhr.open('GET', turnURL, true);
    xhr.send();
  }
}

function handleRemoteStreamAdded(event) {
  console.log('Remote stream added.');
  remoteStream = event.stream;
  remoteVideo.srcObject = remoteStream;
}

function handleRemoteStreamRemoved(event) {
  console.log('Remote stream removed. Event: ', event);
}

function hangup() {
  console.log('Hanging up.');
  stop();
  sendMessage('bye');
}

function handleRemoteHangup() {
  console.log('Session terminated.');
  stop();
  isInitiator = false;
}

function stop() {
  isStarted = false;
  pc.close();
  pc = null;
}

 

 

참고 : forest71.tistory.com/211?category=788767

 

 

728x90
728x90

'Project' 카테고리의 다른 글

[스크랩] Dark Glassmorphism UI & Black Web Design  (0) 2021.05.24
화상채팅 줌 (ZOOM) 프로그램 분석  (0) 2021.03.11
블로그 이미지

coding-restaurant

코딩 맛집에 방문해주셔서 감사합니다.

,
728x90
728x90
728x90
728x90
블로그 이미지

coding-restaurant

코딩 맛집에 방문해주셔서 감사합니다.

,

[c#] yield

C# 2021. 3. 11. 17:06
728x90
728x90

yield


집합적인 데이터셋으로부터 데이터를 하나씩 호출자에게 보내주는 yield는 Iterator, Enumerator라고 불린다

c# yield 키워드는 호출자(caller)에게 컬렉션 데이터를 하나씩 리턴할 때 사용

 

 

사용 방식


1)yield return 혹은 2)yield break 두 가지 방식으로 사용된다.

˙ yield return : 컬렉션 데이터를 하나씩 리턴할 때 사용

˙ yield break : 리턴을 중지하고 Iteration 루프를 빠져나올 때 사용

 

 

사용처


1. 데이터의 양이 커서 나눠서 리턴하는 것이 효율적일 때, 일부만 조회 필요할 때

2. 어떤 메서드가 데이터를 무제한 계속 리턴할 경우

3. 모든 데이터를 미리 계산하기에 느려져서 그때마다 On Demand로 처리하는 것이 좋을 때
(소수(Prime Number)를 계속 리턴하는 함수의 경우 소요시간을 분산하는 지연계산 등을 구현)

 

 

예시 1 


namespace YieldTest
{
  class Program
  {
    static void Main (string[] args)
    {
      var scores = GetAllScores ();

    // 통째로 가져온다
      foreach (var score in GetAllScores())
      {
	       Console.WriteLine (socre);
      } 
      
      // 하나씩 가져온다 
      // 진행상태를 기억한다
      foreach (var score in GetScores())
      {
	    Console.WriteLine (socre);
      }
    }

    static IEnumerable<int> GetScores ()
    {
      int[] scores = new int[]{ 10, 20, 30 };
      yield return scores[0];
      yield return scores[1];
      yield return scores[2];
    }

    static int[] GetAllScores ()
    {
      int[] scores = new int[]{ 10, 20, 30 };
      return scores;
    }
  }
}

 

 

예시 2


namespace YieldTest
{
  class Program
  {
    static void Main (string[] args)
    {
      foreach (var score in GetScores())
      {
	    Console.WriteLine (socre);
      }
    }

    static IEnumerable < int >GetScores ()
    {
      int[] scores = new int[]{ 10, 20, 30 };
      
        for(int i=0; i<scores.Length; i++) {
            yield return scores[i];
            Debug.WriteLine(i+ " : Done");
        }
    }
  }
}

 

 

 

예시 3


namespace YieldTest
{
  class Program
  {
    static void Main (string[] args)
    {
      foreach (var score in GetScores())
      {
	    Console.WriteLine (socre);
      }
    }

    static IEnumerable < int >GetScores ()
    {
      int[] scores = new int[]{ 10, 20, 30, 40};
      
        for(int i=0; i<scores.Length; i++) 
        {
            if(scores[i]== 30) {
                yield break;
            }
            yield return scores[i];
            Debug.WriteLine(i+ " : Done");
        }
    }
  }
}

 

 

 

예시 4


// 누적 합계를 구하는
namespace YieldTest
{
  class Program
  {
    static void Main (string[] args)
    {
      foreach (var total in 누적합계())
      {
	    Console.WriteLine (total);
      }
    }

    static IEnumerable < int > 누적합계 ()
    {
      int[] scores = new int[]{ 10, 20, 30, 40};
      
      	int total = 0;
        
        for(int i=0; i<scores.Length; i++) 
        {
           total += scores[i];
           yield return total;
        }
    }
  }
}

 

 

 

www.csharpstudy.com/CSharp/CSharp-yield.aspx

728x90
728x90
블로그 이미지

coding-restaurant

코딩 맛집에 방문해주셔서 감사합니다.

,
728x90
728x90

 


ZOOM 이란?


Zoom Video Communications에서 개발한 화상 통화 소프트웨어 프로그램으로,
무료 버전은 40분 제한으로 최대 100명 참가 가능 서비스 제공
지원 운영체제는 윈도우, 맥OS, 리눅스, 안드로이드, IOS, 크롬OS, 제공 언어 11가지
비슷한 프로그램으로 마이크로소프트 팀즈, 팀뷰어 등이 있다.

 

첫 실행 / 회의참가 / 로그인 화면

 

회원가입 화면

 

이동 화면

 

여시겠습니까? 팝업창을 띄운 후 프로그램으로 이동한다.

 


줌 회의 실행 화면


 

줌 미팅즈 실행 화면
줌 미팅즈 실행 / 설정 화면

 

 

세부 분석

 

중앙 버튼 컬러 : #0E72ED , (hover 컬러 : #1A6CDA ), #F26D21

상단 바 배경 : #F2F2F5
상단 바 아이콘 : #0E71EB
검색 배경 : #DEDEE3

접속중 표시 : #3FD478

설정 아이콘 : #747487

 

 

 

좌측 배경색 : #2B2B47
접속중 표시 : #3FD478 

 

 

 

 

메인 컬러 : #0E71EB

시작 아이콘 컬러 : #0E71EB  (hover : #0D68D8 )
비활성화 아이콘 컬러 : #232333 

기록됨 배경 컬러 : #F2F2F7

내 개인 회의 타이틀 컬러 : #39394D

 

 


화상회의 화면


 

 

액센트 컬러 : #23D959

버튼 컬러 : #A7A7A7

종료 버튼 배경색 : #B72525

 

 


줌 다운로드


zoom.us/download

 

비디오 회의, 웹 회의, 웨비나, 화면 공유

Zoom은 모바일, 데스크톱 및 회의실 시스템에서 비디오 및 오디오 회의, 채팅 및 웨비나를 안전하고 편리하게 진행할 수 있는 클라우드 플랫폼을 제공하여 첨단 엔터프라이즈 비디오 통신을 선도

zoom.us

 

728x90
728x90
블로그 이미지

coding-restaurant

코딩 맛집에 방문해주셔서 감사합니다.

,

[JS] substr()

Javascript 2021. 3. 11. 10:29
728x90
728x90
String.prototype.substr()

 

문법

 

str.substr(start[, length]);

 

start : 문자 인덱스입니다. 문자열에서 첫 번째 문자의 인덱스는 0이며, 마지막 문자의 인덱스는 문자열 전체 길이에서 1을 뺀 값입니다. substr()는 start에서 문자들을 추출을 시작하여 length만큼 문자들을 수집합니다. start 값이 양수이고 문자열 전체 길이보다 크거가 같을 경우, substr()은 빈 문자열을 반환합니다.

만약 start가 음수이면, substr()은 문자열 끝에서 start 숫자만큼 뺀 곳에서 시작하게 됩니다. 만약 start가 음수이고 절대값이 문자열 전체보다 크다면, substr()은 문자열의 0 인덱스부터 시작하게 됩니다. (주의: start의 음수값은 Microsoft JScript에서는 위의 설명과 같이 동작하지 않습니다.)

추출하고자 하는 문자들의 시작위치입니다. 만약 음수가 주어진다면, 문자열총길이 + start의 값으로 취급합니다. 예를 들면, start에 -3을 설정하면, 자동적으로 문자열총길이 - 3으로 설정하게 됩니다. 

length : 옵션값. 추출할 문자들의 총 숫자.  만약 length가 0 혹은 음수이면, substr()은 빈 문자열을 반환합니다. 만약 length가 생략되면, substr()은 문자열의 끝까지 추출하여 반환합니다. 

 

예시

 

const str = 'Mozilla';

console.log(str.substr(1, 2));
// expected output: "oz"

console.log(str.substr(2));
// expected output: "zilla"

 

 

728x90
728x90

'Javascript' 카테고리의 다른 글

  (0) 2021.03.17
스택으로 뒤로가기/앞으로가기  (0) 2021.03.16
select box 셀렉트 박스 변경 이벤트 처리(js, jquery)  (0) 2021.02.26
임의로 클릭 발생, 제이쿼리 trigger  (0) 2021.02.22
[JS] spread (...) 문법  (0) 2021.02.18
블로그 이미지

coding-restaurant

코딩 맛집에 방문해주셔서 감사합니다.

,
728x90
728x90
비쥬얼 스튜디오를 사용

 

비쥬얼스튜디오 여러줄 주석

1. Ctrl + K + C : 자신이 드래그한 영역 주석 처리
2. Ctrl + K + U : 자신이 드래그한 영역 주석 해제

 

프로그램을 컴파일, 시작하는 방법

1 코드 작성 후 프로그램 실행은 Build > Build Solution (F6) 
2 우측 탭의 프로젝트에서 마우스 우클릭해서 빌드 선택
3 Debug > Start Without Debugging
4 Debug > Start With Debugging (숫자 앞 빈칸에 중단점을 찍고 시작)

 

https://www.youtube.com/watch?v=NZ0csWCxD3g&list=PLiNvMfa_Y5hdcP86bazUSDpvroNR9HpNY

 

 

 

 

콘솔명령 아규먼트 사용과 지정

아규먼트란 프로그램을 시작할 때 1.txt, 2.txt 등 실행할 때 뒤에 붙여주는 것. 

 

 

비쥬얼 스튜디오, 콘솔 프로그램으로 생성

 

 

아규먼트 사용 시 주의점 : null일 경우 처리를 해 준다. (args.Length<x)

 

using System;
using System.IO;

namespace ConsoleApp2
{
    class Program
    {
        //args란 문자열배열을 받고 있음
        static void Main(string[] args)
        {            
            // 사용자가 두개 미만의 파라미터를 넣었을 때 에러 방지
            if(args.Length<2)
            {
                Console.WriteLine("Usage: C>ConsoleApp2 srcfile destfile");
                return;
            }

            // 파라미터
            string srcfile = args[0]; //1.txt
            string destfile = args[1]; //2.txt

            // 소스파일이름
            File.Copy(srcfile, destfile);
            
            Console.WriteLine($"Copy {srcfile} to {destfile}");

        }
    }
}

 

불필요한 using은 입력 화면 우클릭 > Remove and Sort Usings(Ctrl + R, Ctrl + G)를 선택하여 지워준다.

 

 

빌드

 

 

 

exe파일 전까지 위치를 긁어서 찾아간다

 

cd 경로
경로 dir

 

 

 

 

ConsoleApp2.exe를 실행, ConsoleApp2.pdb 파일을 ConsoleApp2.pdb2로 복사해본다.

 

 

 

일일히 이렇게 할 수 없으니까 비쥬얼스튜디오 내에서 아규먼트를 지정하려면

비쥬얼 스튜디오 우측 탭 > 솔루션 아래 ConsoleApp2 우클릭 > '속성' 선택 > 디버그 > command line arguments : 에 1.txt 2.txt 라고 아규먼트를 준다.

 

(좌) 우클릭 할 곳 (우) 속성 클릭 후 나타난 창에서 변경해야 할 부분

 

cmd창에 copy 경로 및 파일명 으로 테스트 파일 복사 후 코드 실행

 

 

 

윈폼 프로그램

 

새 프로젝트 생성

 

 

비어있는 폼이 생성된다

 

 

ui는 좌측 탭의 툴박스(Toolbox)를 사용해 드래그앤드롭으로 구성할 수 있다.

 

 

텍스트박스와 버튼을 놓아봤다

search toolbox에서 검색도 가능

 

 

우측 하단에서 텍스트 값, 속성 값 등을 변경 가능

각 textarea의 속성을 txtUsername, txtPassword, 버튼의 속성을 btnOK로 바꿔준다

 

 

라벨을 하나 더 만들어서 제목을 만들어주고

F5 를 눌러서 실행

 

 

팝업창을 닫고 종료시키기

버튼을 눌렀을 때 뭔가가 되게 해보자

 

 

버튼을 더블클릭하면 코드가 나옴

아래와 같이 수정

 

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace MyWinForm
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

        private void label1_Click(object sender, EventArgs e)
        {

        }

        private void label3_Click(object sender, EventArgs e)
        {

        }

        private void btnOK_Click(object sender, EventArgs e)
        {
            if (txtUsername.Text == "admin" && txtPassword.Text == "1234")
            {
                MessageBox.Show("성공");

            }else
            {
                MessageBox.Show("실패", "에러"); //뒷부분은 결과 팝업의 타이틀
            }
        }
    }
}

 

테스트

 

 

 

연결된 이벤트 핸들러

 

 

 

기본 구성격인 파일들이다

 

 

 

Designer.cs 파일은 자동 작성되는 파일이며 건들지 않는 게 좋다

 

 

 

본 요약글은 유튜버 csharpstudy의 영상을 보고 실습하면서 작성되었습니다.

 

www.youtube.com/playlist?list=PLiNvMfa_Y5hdcP86bazUSDpvroNR9HpNY

728x90
728x90
블로그 이미지

coding-restaurant

코딩 맛집에 방문해주셔서 감사합니다.

,

v