Data is not code
The rule is one sentence: user text must arrive as data, never as markup. Escaping is how you enforce it. Any place you splice raw text into HTML, or set innerHTML from user input, is an XSS hole.
Attributes too
Escaping < is not enough inside an attribute, a value can break out with a quote. Escape quotes as well, or set the attribute through an API that quotes for you. Attribute injection is XSS by another door.