Redis 란?

Redis 는 Key-Value 형태로 데이터를 관리하는 오픈 소스이다. Redis 는 빠른 속도와 간편한 사용법으로 인해 캐시, 인증 토큰, 세션 관리 등등 여러 용도로 사용되고 있다. Redis는 빠른 속도 대신 서버가 꺼지면 데이터가 휘발되는 특징이 있는데, 이번 프로젝트에선 Redis를 채팅룸 저장과 GameStartSet을 잠시 저장하는 DB로 사용하는 용도로 채택하였다. 

  • In-Memory Data Strucutre Store 
  • Key - Value 형태로 데이터 저장
  • 여러 가지 Value 타입 저장 가능 (String, Set, Hash, List 등등..)
  • Single Thread
  • 데이터 만료 시간 지정 가능 -> refresh토큰에 사용된다고 한다.

1. macOS에 Redis 설치

 

2. 전제 조건

Homebrew가 설치되어 있는지 확인하기위해 터미널에서 다음을 실행한다.

  • brew --version

 

3. 설치

터미널에서 다음을 실행한다. 시스템에 Redis가 설치된다.

 
  • brew install redis

 

4. Redis 시작 및 중지하기

1) Redis 설치를 테스트하기 위해 터미널창에 다음을 입력한다. 만약 정상적으로 실행이 된다면, log가 쭈루루룩 올라오는 것을 확인할 수 있다. 중지하려면 Ctrl-C를 입력한다.

  • redis-server

2) 위의 명령어를 대신해 아래와 같은 명령어로 실행할 수도 있다. (포그라운드에서 Redis를 실행하는 대신 launchd를 사용하여 프로세스를 백그라운드에서 시작할 수도 있다. 이렇게 하면 Redis가 시작되고 로그인 시 다시 시작된다.)

 
  • brew services start redis

3) 중지하려면 아래의 명령어를 입력한다.

  • brew services stop redis

 

5. Redis 연결

아래의 명령어로 레디스에 연결한다.

  • redis-cli

만약 한국어깨짐 현상이 있다면 아래 명령어를 입력해 준다.

  • redis-cli --raw

 

6. Redis 명령어

참고 링크 -> https://redis.io/commands/

1) String

  • 저장
    • set {key} {value} : key, value 를 저장
    • mset {key} {value} [{key} {value} ...] : 여러 개의 key, value 를 한번에 저장
    • setex {key} {seconds} {value} : key, seconds, value 저장 (설정한 시간 뒤에 소멸)
  • 조회
    • keys * : 현재 저장된 키값들을 모두 확인 (부하가 심한 명령어라 운영중인 서비스에선 절대 사용하면 안됨)
    • get {key} : 지정한 key 에 해당하는 value 를 가져옴
    • mget {key} [{key} ...] : 여러 개의 key 에 해당하는 value 를 한번에 가져옴
    • ttl {key} : key 의 만료 시간을 초 단위로 보여줌 (-1 은 만료시간 없음, -2 는 데이터 없음)
    • pttl {key} : key 의 만료 시간을 밀리초 단위로 보여줌
    • type {key} : 해당 key 의 value 타입 확인
  • 삭제
    • del {key} [{key} ...] : 해당 key 들을 삭제
  • 수정
    • rename {key} {newKey} : key 이름 변경
    • expire {key} {seconds} : 해당 키 값의 만료 시간 설정
  • 기타
    • randomkey : 랜덤한 key 반환
    • ping : 연결 여부 확인 ("ping" 만 입력하면 "PONG" 이라는 응답이 옴)
    • dbsize : 현재 사용중인 DB 의 key 의 갯수 리턴
    • flushall : 레디스 서버의 모든 데이터 삭제
    • flushdb : 현재 사용중인 DB 의 모든 데이터 삭제

 

2) Set

Redis 에서는 Set 에 포함된 값들을 멤버라고 표현한다. 여러 멤버가 모여 집합 (Set) 을 구성한다.

  • sadd {key} {member} [{member} ...]
    • key 에 새로운 멤버들을 추가. key 가 없으면 새로 만듬
  • smembers {key}
    • key 에 설정된 모든 멤버 반환
  • srem {key} {member [{member} ...]}
    • key 에 포함된 멤버들 삭제. 없는 멤버 입력하면 무시됨
  • scard {key}
    • key 에 저장된 멤버 수를 반환
  • sismember {key} {member}
    • member 가 해당 key 에 포함되는지 검사

 

3) Hash

Hash 자체를 나타내는 key 와 해당 key 에 포함된 field 까지 사용해서 값을 조회/저장할 수 있습니다.

  • hset {key} {field} {value} [{field} {value} ...]
    • key 를 이름으로 한 Hash 자료 구조에 field 와 value 값을 저장
  • hget {key} {field}
    • key Hash 값에 포함된 field 의 value 를 가져옴
  • hdel {key} {field} [{field} ...]
    • field 값으로 데이터 삭제
  • hlen {key}
    • Hash 가 갖고 있는 field 갯수 반환
  • hkeys {key}
    • Hash 가 갖고 있는 모든 field 출력
  • hvals {key}
    • Hash 가 갖고 있는 모든 value 출력
  • hgetall {key}
    • Hash 가 갖고 있는 모든 field 와 value 출력

 

7. Redis 사용시 발생한 문제, 해결 하지 못함

 

 

위와 같이 hgetall {Key} 로 조회했을 때, 내용이 깨져서 나오는 문제가 있다. 일단 한글은 redis-cli --raw 명령어를 사용해서 깨지지 않고 잘 나오고 있는데, 다른 부분들이 다 저렇게 알 수 없는 문자들로 나온다 ... 구글링을 반나절 한 결과 Medis 라는 Redis GUI 프로그램을 찾긴 했지만 $5 구독비가 있기 때문에 일단 사용을 보류하기로 했다. 해결 방법 아시는분 제발 댓글 좀 부탁드립니다.

아래는 Redis GUI 참고 블로그 

 

Mac M1에서 가능한 Redis GUI Client 프로그램 : Medis2

이제 좀 redis를 알아보려고 하는데, 계속 터미널에서 확인해야하니 명령어를 계속 찾아봐야해서 너무 번거로웠다.. 그래서 뭔가 gui툴이라던지 다른 프로그램이 없을까봐 찾아봤다. 그중에서 내

pearlluck.tistory.com


[ 참고자료 ]

 

Redis 설치 및 명령어

1. Redis 란? Redis 는 Key-Value 형태로 데이터를 관리하는 오픈 소스입니다. Redis 는 빠른 속도와 간편한 사용법으로 인해 캐시, 인증 토큰, 세션 관리 등등 여러 용도로 사용됩니다. In-Memory Data Strucutre

bcp0109.tistory.com

 

Git vs GitHub

Git

  • Git is a distributed version control system for tracking changes in source code during software development.
  • It is designed for coordinating work among programmers, but it can be used to track changes in any set of files.
  • Its goals include speed, data integrity, and support for distributed, non-linear workflows.
  • Git은 소프트웨어 개발 중에 소스 코드의 변경 사항을 추적하기 위한 분산 버전 제어 시스템입니다. 
  • 프로그래머 간의 작업 조정을 위해 설계되었지만 모든 파일의 변경 사항을 추적하는 데 사용할 수 있습니다.
  • 그 목표에는 속도, 데이터 무결성 및 분산된 비선형 워크플로우에 대한 지원이 포함됩니다.

GitHub

  • GitHub is a web-based Git repository hosting service, which offers all of the distributed revision control and source code management (SCM) functionality of Git as well as adding its own features. 
  • GitHub는 웹 기반 Git 리포지토리 호스팅 서비스로, 자체 기능을 추가할 뿐만 아니라 Git의 모든 분산 개정 제어 및 소스 코드 관리(SCM) 기능을 제공합니다. 
Git GitHub
소프트웨어이다. 서비스이다.
시스템에 로컬로 설치된다. 웹에서 호스팅된다.
Linux에서 관리한다. Microsoft에서 관리한다.
소스 코드 히스토리를 관리하기 위한 버전관리 시스템 Git 리포지토리를 위한 호스팅 서비스
사용자 관리 기능이 없다. 사용자 관리 기능이 내장되어 있음

 

Git-flow vs Github-flow

Git-flow

  • Git-flow는 브랜치를 크게 4가지로 나누어 개발하는 전략입니다.
    • 메인 브랜치(Main branch)
    • 피처 브랜치(Feature branch) 또는 토픽 브랜치(Topic branch)
    • 릴리스 브랜치(Release branch)
    • 핫픽스 브랜치(Hotfix branch)
  • 가장 중심이 되는 브랜치는 masterdevelop 브랜치이며,  merge된 feature, release, hotfix 브랜치는 삭제하도록합니다.
  • Git-flow 전략은 주기적으로 배포를 해야하는 프로젝트에는 적합하지만, 브랜치가 많아 복잡하고 어떤 프로젝트에 따라서는 몇몇 브랜치가 애매한 포지션을 가질 수 있습니다.
  • Git Flow is usually more complicated than GitHub flow. It is used when your software has the concept of “release”. This flow works perfectly when you work in a team of one or more developers and they collaborate on the same feature.

 

Github-flow

 

  • Git-flow가 Github에서 사용하기에는 복잡하다고 나온 브랜칭 전략입니다.
  • 흐름이 단순한 만큼 role도 단순합니다. master 브랜치에 대한 role만 정확하다면 나머지 브랜치들에 대해서는 관여하지 않습니다.
  • 즉, hotfix 브랜치나 feature 브랜치를 구분하지 않습니다. 다만 우선순위가 다를 뿐입니다. pull request 기능을 사용하도록 권장합니다.
  • 이 브랜칭 전략은 수시로 배포가 일어나며, CI와 배포가 자동화되어있는 프로젝트에 유용합니다.
  • GitHub flow is really very simple to adapt as this flow doesn’t have releases because your deployment to production happens every day. There are two types of branches involved in this flow, the master branch, and the feature branch.

 


[ 참고 자료 ]

 

코딩도 대신해주는 너란 녀석, 엄청나다.

 

 

요즘 노마트코더를 비롯한 개발자들의 유튜브 채널에 자주 등장하는 화제인 ChatGPT!

코드도 작성해주고, 만든 코드를 개선시켜 주기도하고, 다른 언어로 바꿔주는 등 인공지능으로 다양한 서비스를 제공한다고 한다. 

이번 프로젝트를 하면서, WebRTC와 관련된 예제코드들이 거의다 Node.js로 작성되어 있었는데, 도무지 이해할 수 없어서 이참에 한번 ChatGPT를 사용하여 Java로 언어변환을 시도해봤다! 비록 코드가 완전히 정확하진 않았지만, 그래도 뭔가 길을 안내해주는 (?) 역할은 한 것 같다.  완전 신기!

 

언어를 전공했는데 구글 번역기 등장으로 통번역가 일자리가 많이 줄더니, 코딩을 시작하자마자 바로 다음달에 ChatGPT가 등장한 My Life ..... ^__^  뭐 그래도 괜찮다. 오히려 좋아 ~ 잘 활용하면 인공지능을 통해 더 빨리 배울 수 있을 것 같다. 이거 만든 개발자들은 도대체 어떤 사람들일까 궁금하다. 파이썬을 공부해야 하나 ? ^___^ 허허

 

모든 개발자들 화잇팅 ! 


개념

  • ChatGPT는 사용자와 주고받는 대화에서 질문에 답하도록 설계된 언어모델이다. 오픈AI는 블로그 게시글을 통해 “챗GPT가 대화 형식으로 추가적인 질문에 답하고 실수를 인정하며 정확하지 않은 전제에 대해서는 이의를 제기하고 부적절한 요청을 거부할 수 있다”고 설명했다.

특징

  • 답변이 마음에 들지 않으면 Try Again 버튼으로 다른 답변을 요청할 수 있으며, 현재 답에 대해 긍정 또는 부정으로 평가할 수 있다.
  • 가능한 한 답변을 제공하려고 노력하나, 정치 색, 헤이트 스피치, 선정성등 사회통념상 논란이나 거부감이 들어간 답변은 거부하는 경우가 상당히 많다.
  • 기본적으로 간단한 개념에 대한 설명을 요청할 수 있으며, 특정 개념들에 대한 비교, 공통점, 차이점 등을 요청할 수도 있다.

잠재성 및 한계

  • ChatGPT가 가진 잠재력은 무시하지 못할 수준인데, 구글의 모회사인 알파벳 CEO가 직접 언급할 정도로 경계하고 있을 정도다. 데이터가 더 쌓여 한계를 극복한다면 사용자가 직접 검색할 대상의 정보를 입력해야 하는 현 검색 문법을 아예 뒤바꿀 기술이 될 것이기 때문에, 장기적으로는 구글을 비롯한 모든 검색 엔진을 대체 할 수 있는 기술이기 때문이다. 하지만 이러한 잠재적인 문제(영어)도 존재하기 때문에 섣부른 예측은 금물이다. 빅데이터 봇이라 그런지 영어로 질문하면 한국어로 질문할 때보다 정확도가 올라가긴 하지만, 100% 정확한 건 아니다.
  • 특히 기계학습 인공지능 모델들의 고질적 문제인 편향성 문제에서 자유롭지 못하다. 인공지능이 가치중립적일 것 처럼 보여도 결국 인공지능이 학습하는 것은 인간이 만든 데이터들이기 때문에 편향성에서 자유로울 수가 없다. 비교적 그런 문제에서 자유로운 편인 그림, 음악, 게임 알고리즘 부류와 달리 온갖 지식을 다루는 ChatGPT 특성상 편향성 문제가 강하게 드러날 잠재적 위험성이 크다. 게다가 제작진이 직접 개입하여 금지 사항을 정하고 가이드라인을 주입하기 때문에 더더욱 인위적인 편향성 문제가 생겨난다. 즉, 논란의 여지가 많은 질문일수록 대답을 신뢰하지 않는 것이 좋다.

 

출처 : https://namu.wiki/w/ChatGPT


 

ChatGPT 사이트 : https://openai.com/blog/chatgpt/

 

ChatGPT: Optimizing Language Models for Dialogue

We’ve trained a model called ChatGPT which interacts in a conversational way. The dialogue format makes it possible for ChatGPT to answer followup questions, admit its mistakes, challenge incorrect premises, and reject inappropriate requests. ChatGPT is

openai.com

위키피디아 설명 : https://en.wikipedia.org/wiki/ChatGPT

 

ChatGPT - Wikipedia

From Wikipedia, the free encyclopedia Jump to navigation Jump to search Artificial intelligence chatbot ChatGPT (Generative Pre-trained Transformer)[1] is a chatbot launched by OpenAI in November 2022. It is built on top of OpenAI's GPT-3 family of large l

en.wikipedia.org

 

'Coding > 기타' 카테고리의 다른 글

[ DataBase ] RDB, NOSQL, In-Memory  (0) 2023.01.22
WebServer vs Was  (0) 2023.01.13
HTTP 이해  (0) 2022.12.06
기술매니저님 추천 사이트 ( Spring 관련 )  (0) 2022.12.02
RESTful API 설계 가이드  (0) 2022.12.01

 

 

 

1. 어려웠던 부분 : 지난주 토요일 기술멘토링에서 백엔드 스코프가 비교적 작다는 피드백을 받아서 오늘은 추가적으로 구현해볼 사항을 팀원들과 논의했다. WebRTC와 WebSocket에 대한 개념이 제로일때 스코프를 짰던거라 백엔드 스코프가 작은 줄 몰랐었는데, 프론트엔드에서 대부분 구혀을 맡아준다는 걸 시작을 하고나서야 알게되다보니 멘토님 말씀처럼 수정이 필요한긴 했다. 근데 지금 프론트엔드에서 해야할 부분이 너무 많기 때무에 최대한 프론트엔드분들에게 덜 영향을 줄 수 있는 방향으로 스코프를 확장하고자 했다. 그래서 나온 의견은 

1. 인프라 쪽 확장하기 ( CI/CD, Doker)

2. 모두에게 익숙한 게시판 기능 추가하기 

3. 마이페이지 기능 추가하기 ( 회원탈퇴, 닉네임 변경, 업적 추가)

 

2. 느낀 점 : PostMan으로 백엔드 코드를 테스트 했을 때는 문제가 없더라고 막상 프론트엔드와 붙여봤을때 다양한 에러와 반환값 변경과 같은 수정사항이 생긴다. 프로젝트를 하면서 정말 소통이 중요하다는 것을 하루하루 몸소 느끼고 있다. 의사를 확실히 전달하면서 예쁘게 말하는게 얼마나 중요한지 배우고 있다. 모두 열심히 해주는 팀원들에게 감사하다.

 

3. 새로 알게 된 내용 : 내가 담당한 부분이 비교적 작은편이라 팀원들에게 터지는 에러를 같이 찾아보고 해결해 주고있다. 오늘은 트러블 슈팅 세가지를 정리해 봤는데 (아래 링크 참조) 나의 분량이 작더라도 팀원들에게 터지는 에러를 같이 찾으면서 많은 공부가 되는 것 같다. 그리고 지난주 토요일에 멘토님이 숙제로 내주신 Spring 과 Spring boot 차이점에 대해 공부해 봤다. Spring boot는 개발자가 보다 Spring 프레임 워크를 편리하게 사용하기 위해 확장된 프레임워트라는 사실 ! 나중에 면접을 위해서 각각의 특징을 잘 숙지해놔야 겠다.

 

4. 셀프칭찬 (오늘 잘한 일) : 맡은 부분을 잘 끝내고, 팀원들에게 발생한 에러들을 같이 해결했다. 분량이 작다고 걱정할게 아니라 더 열심히 히 다른사람이 맡은 부분을 공부해야 겠다.

 

5. 내일 할 일 : WebRTC 시그널링 에러 도와서 같이 해결해보기 ! 


[오늘 공부한 부분] 

[41] 트러블 슈팅 : nonuniqueresultexception: query did not return a unique result

 

[41] 트러블 슈팅 : nonuniqueresultexception: query did not return a unique result

문제 : 인텔리제이 콘솔에 다음과 같은 에러 메세지가 떴다 : nonuniqueresultexception: query did not return a unique result 원인 : Database Corruption 가 원인이다. 즉, 이 에러는 DB에서 조회한 결과가 중복이 될 때

leejincha.tistory.com

[42] 트러블 슈팅 : Type definition error

 

[42] 트러블 슈팅 : Type definition error

문제 : 게임방 생성 API test시 Type definition error 발생 원인 : domain 생성자를 인식하지 못해서 생기는 에러, @Builder 어노테이션을 사용하고 있는데 생성자를 만들어주는 부분이 없다면 발생할 수 있

leejincha.tistory.com

[43] 트러블 슈팅 : @RequestMapping의 produces 속성

 

[43] 트러블 슈팅 : @RequestMapping의 produces 속성

문제 프론트엔드의 요청으로 반환값으로 String 타입의 nickname 을 return 하는 상황 PostMan으로 테스트 한 결과, 한글과 띄어쓰기가 모두 ?로 반환되었다. 해결 @RequestMapping의 produces 속성을 이용해 Resp

leejincha.tistory.com

[44] Spring vs Spring boot 차이

 

[44] Spring vs Spring boot 차이

Spring 이란 ? 스프링 프레임워크(Spring Framework)는 자바 플랫폼을 위한 오픈소스 애플리케이션 프레임워크로서 간단히 스프링(Spring)이라고도 불린다. 동적인 웹 사이트를 개발하기 위한 여러 가지

leejincha.tistory.com

 

Spring 이란 ? 

  • 스프링 프레임워크(Spring Framework)는 자바 플랫폼을 위한 오픈소스 애플리케이션 프레임워크로서 간단히 스프링(Spring)이라고도 불린다.
  • 동적인 웹 사이트를 개발하기 위한 여러 가지 서비스를 제공하고 있다. 
  • Spring is an open-source lightweight framework that allows Java EE 7 developers to build simple, reliable, and scalable enterprise applications.
  •  It made the development of Web applications much easier than compared to classic Java frameworks and Application Programming Interfaces (APIs), such as Java database connectivity (JDBC), JavaServer Pages(JSP), and Java Servlet.
  • The Spring framework can be considered as a collection of sub-frameworks, also called layers, such as Spring AOP. Spring Object-Relational Mapping (Spring ORM). Spring Web Flow, and Spring Web MVC.

특징

  • 경량 컨테이너
  • IoC(Invertion of Control: 제어 역행) :  IoC는 컨트롤의 제어권이 개발자에게 있는 것이 아닌 프레임워크가 대신해서 해주는 것을 말합니다. Servlet이나 Bean 같은 코드를 개발자가 직접 작성하지 않고, 프레임워크가 대신 수행합니다. 제어의 역전이라는 말이 어려울 수 있는데, 기존에는 자바 코드를 작성할 때 객체의 생성, 의존관계 설정 등을 개발자가 해줘야 했지만, 프레임워크가 대신해준다는 의미입니다.
  • Di(Dependency Injection: 의존성 주입) : DI란 개발자가 Spring 프레임워크에 의존성을 주입하면서 객체 간 결합을 느슨하게 하는 것입니다. 객체 간 결합이 느슨하면 코드의 재사용성이 증가하고, 단위 테스트가 용이해집니다.
  • AOP(Aspect-Oriented Programming: 관점지향 프로그래밍) : AOP는 핵심기능을 제외한 부수적인 기능을 프레임워크가 제공하는 특징입니다. 예를 들어 Spring 프로젝트에 security를 적용하거나, logging 등을 추가하고 싶을 때 기존 비즈니스 로직을 건들지 않고 AOP로 추가할 수 있습니다.
  • 중복 코드 제거 : JDBC 같은 템플릿을 사용할 때 중복되는 코드도 많고 복잡합니다. 이를 모두 제거합니다.

 

Spring Boot 란?

  • 스프링 부트는 스프링 프레임워크를 사용하기 위한 설정의 많은 부분을 자동화하여 사용자가 정말 편하게 스프링을 활용할 수 있도록 돕는다. 실행환경이나 의존성 관리 등의 인프라 관련 등은 신경쓸 필요 없이 바로 코딩을 시작하면 된다. 
  • Spring Boot는 기본적으로 Spring Framework를 설정하는데 필요한 구성을 제거하는 Spring Framework의 확장버전이다.
  • 개발자 입장에서 더 빠르고 효율적으로 프로그램 개발이 가능하도록 도와준다.
  • Spring Boot is built on top of the conventional spring framework.
  • So, it provides all the features of spring and is yet easier to use than spring.
  • Spring Boot is a microservice-based framework and making a production-ready application in very less time.
  • In Spring Boot everything is auto-configured. We just need to use proper configuration for utilizing a particular functionality. Spring Boot is very useful if we want to develop REST API. 

특징

  • Embed Tomcat을 사용하기 때문에, (Spring Boot 내부에 Tomcat이 포함되어있다.) 따로 Tomcat을 설치하거나 매번 버전을 관리해 주어야 하는 수고로움을 덜어준다.
  • starter을 통한 dependency 자동화
    과거 Spring framework에서는 각각의 dependency들의 호환되는 버전을 일일이 맞추어 주어야 했고, 때문에 하나의 버전을 올리고자 하면 다른 dependeny에 까지 영향을 미쳐 version관리에 어려움이 많았다. 하지만, 이제 starter가 대부분의 dependency를 관리해주기 때문에 이러한 걱정을 많이 덜게 되었다.
더보기
  • spring-boot에서 starter란 의존성과 설정을 자동화해주는 모듈을 뜻한다.

    예를 들어, spring-boot-starter-jpa를 의존성 추가했을 때 아래의 일을 해준다.

    - spring-aop, spring-jdbc 등의 의존성을 걸어준다.
    - classpath를 뒤져서 어떤 Database를 사용하는지 파악하고, 자동으로 entityManager를 구성해준다.
    - 해당 모듈들 설정에 필요한 properties 설정을 제공한다(Configuration Processor를 사용하면 효과 UP)
    - 프로젝트를 진행하면서, 공통적으로 사용되는 spring 설정을 모듈로 묶어놓고 사용할 수 있다.
    - 또한 필요한 경우, 상위 프로젝트에서 얼마든지 설정을 덮어쓸 수 있다.
  • XML설정을 하지 않아도 된다.
  •  jar file을 이용해 자바 옵션만으로 손쉽게 배포가 가능하다.
  • Spring Actuaor를 이용한 애플리케이션의 모니터링과 관리를 제공한다.

 

Difference between Spring and Spring Boot

  • Spring Framework는 기존에 EJB를 대신해 자바 애플리케이션을 더 쉽게 만들 수 있게 해 주고, Spring Boot Framework는 Spring Framework보다 개발자가 더더욱 개발에만 집중할 수 있도록 도와주는 프레임워크이다.
  • 프로젝트에서 필수적인 종속성 구성이 필요하다면 Spring을, 일반적인 종속성 구성으로 구현이 가능한 프로젝트라면 Spring Boot로도 충분한 결과를 얻어낼 수 있다.

 

 


[ 참고자료 ]

+ Recent posts